123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334 |
- const { div, h2, p, section, button, form, a, span, textarea, br, input, label, select, option, img, hr, table, tr, th, td } = require("../server/node_modules/hyperaxe");
- const { template, i18n } = require('./main_views');
- const moment = require("../server/node_modules/moment");
- const { config } = require('../server/SSB_server.js');
- const { renderUrl } = require('../backend/renderUrl');
- const userId = config.keys.id;
- const renderCardField = (labelText, value) =>
- div({ class: 'card-field' },
- span({ class: 'card-label' }, labelText),
- span({ class: 'card-value' }, ...renderUrl(value))
- );
- exports.marketView = async (items, filter, itemToEdit = null) => {
- const list = Array.isArray(items) ? items : [];
- let title = i18n.marketAllSectionTitle;
- switch (filter) {
- case 'mine':
- title = i18n.marketMineSectionTitle;
- break;
- case 'create':
- title = i18n.marketCreateSectionTitle;
- break;
- case 'edit':
- title = i18n.marketUpdateSectionTitle;
- break;
- }
- let filtered = [];
- switch (filter) {
- case 'all': filtered = list; break;
- case 'mine': filtered = list.filter(e => e.seller === userId); break;
- case 'exchange': filtered = list.filter(e => e.item_type === 'exchange' && e.status === 'FOR SALE'); break;
- case 'auctions': filtered = list.filter(e => e.item_type === 'auction' && e.status === 'FOR SALE'); break;
- case 'new': filtered = list.filter(e => e.item_status === 'NEW' && e.status === 'FOR SALE'); break;
- case 'used': filtered = list.filter(e => e.item_status === 'USED' && e.status === 'FOR SALE'); break;
- case 'broken': filtered = list.filter(e => e.item_status === 'BROKEN' && e.status === 'FOR SALE'); break;
- case 'for sale': filtered = list.filter(e => e.status === 'FOR SALE'); break;
- case 'sold': filtered = list.filter(e => e.status === 'SOLD'); break;
- case 'discarded': filtered = list.filter(e => e.status === 'DISCARDED'); break;
- case 'recent':
- const oneDayAgo = moment().subtract(1, 'days').toISOString();
- filtered = list.filter(e => e.status === 'FOR SALE' && e.createdAt >= oneDayAgo);
- break;
- default: break;
- }
- filtered = filtered.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
- return template(
- title,
- section(
- div({ class: "tags-header" },
- h2(i18n.marketTitle),
- p(i18n.marketDescription)
- ),
- div({ class: "filters" },
- form({ method: "GET", action: "/market" },
- button({ type: "submit", name: "filter", value: "all", class: filter === 'all' ? 'filter-btn active' : 'filter-btn' }, i18n.marketFilterAll),
- button({ type: "submit", name: "filter", value: "mine", class: filter === 'mine' ? 'filter-btn active' : 'filter-btn' }, i18n.marketFilterMine),
- button({ type: "submit", name: "filter", value: "exchange", class: filter === 'exchange' ? 'filter-btn active' : 'filter-btn' }, i18n.marketFilterItems),
- button({ type: "submit", name: "filter", value: "auctions", class: filter === 'auctions' ? 'filter-btn active' : 'filter-btn' }, i18n.marketFilterAuctions),
- button({ type: "submit", name: "filter", value: "new", class: filter === 'new' ? 'filter-btn active' : 'filter-btn' }, i18n.marketFilterNew),
- button({ type: "submit", name: "filter", value: "used", class: filter === 'used' ? 'filter-btn active' : 'filter-btn' }, i18n.marketFilterUsed),
- button({ type: "submit", name: "filter", value: "broken", class: filter === 'broken' ? 'filter-btn active' : 'filter-btn' }, i18n.marketFilterBroken),
- button({ type: "submit", name: "filter", value: "for sale", class: filter === 'for sale' ? 'filter-btn active' : 'filter-btn' }, i18n.marketFilterForSale),
- button({ type: "submit", name: "filter", value: "sold", class: filter === 'sold' ? 'filter-btn active' : 'filter-btn' }, i18n.marketFilterSold),
- button({ type: "submit", name: "filter", value: "discarded", class: filter === 'discarded' ? 'filter-btn active' : 'filter-btn' }, i18n.marketFilterDiscarded),
- button({ type: "submit", name: "filter", value: "recent", class: filter === 'recent' ? 'filter-btn active' : 'filter-btn' }, i18n.marketFilterRecent),
- button({ type: "submit", name: "filter", value: "create", class: "create-button" }, i18n.marketCreateButton)
- )
- )
- ),
- section(
- (filter === 'create' || filter === 'edit') ? (
- div({ class: "market-form" },
- form({
- action: filter === 'edit' ? `/market/update/${encodeURIComponent(itemToEdit.id)}` : "/market/create",
- method: "POST",
- enctype: "multipart/form-data"
- },
- label(i18n.marketItemType), br(),
- select({ name: "item_type", id: "item_type", required: true },
- option({ value: "auction", selected: itemToEdit?.item_type === 'auction' ? true : false }, "Auction"),
- option({ value: "exchange", selected: itemToEdit?.item_type === 'exchange' ? true : false }, "Exchange")
- ), br(), br(),
- label(i18n.marketItemTitle), br(),
- input({ type: "text", name: "title", id: "title", value: itemToEdit?.title || '', required: true }), br(), br(),
- label(i18n.marketItemDescription), br(),
- textarea({ name: "description", id: "description", placeholder: i18n.marketItemDescriptionPlaceholder, rows: "6", innerHTML: itemToEdit?.description || '', required: true }), br(), br(),
- label(i18n.marketCreateFormImageLabel), br(),
- input({ type: "file", name: "image", id: "image", accept: "image/*" }), br(), br(),
- label(i18n.marketItemStatus), br(),
- select({ name: "item_status", id: "item_status" },
- option({ value: "BROKEN", selected: itemToEdit?.item_status === 'BROKEN' ? true : false }, "BROKEN"),
- option({ value: "USED", selected: itemToEdit?.item_status === 'USED' ? true : false }, "USED"),
- option({ value: "NEW", selected: itemToEdit?.item_status === 'NEW' ? true : false }, "NEW")
- ), br(), br(),
- label(i18n.marketItemStock), br(),
- input({
- type: "number",
- name: "stock",
- id: "stock",
- value: itemToEdit?.stock || 1,
- required: true,
- min: "1",
- step: "1"
- }), br(), br(),
- label(i18n.marketItemPrice), br(),
- input({ type: "number", name: "price", id: "price", value: itemToEdit?.price || '', required: true, step: "0.000001", min: "0.000001" }), br(), br(),
- label(i18n.marketItemTags), br(),
- input({ type: "text", name: "tags", id: "tags", placeholder: i18n.marketItemTagsPlaceholder, value: itemToEdit?.tags?.join(', ') || '' }), br(), br(),
- label(i18n.marketItemDeadline), br(),
- input({
- type: "datetime-local",
- name: "deadline",
- id: "deadline",
- required: true,
- min: moment().format("YYYY-MM-DDTHH:mm"),
- value: itemToEdit?.deadline ? moment(itemToEdit.deadline).format("YYYY-MM-DDTHH:mm") : ''
- }), br(), br(),
- label(i18n.marketItemIncludesShipping), br(),
- input({ type: "checkbox", name: "includesShipping", id: "includesShipping", checked: itemToEdit?.includesShipping }), br(), br(),
- button({ type: "submit" }, filter === 'edit' ? i18n.marketUpdateButton : i18n.marketCreateButton)
- )
- )
- ) : (
- div({ class: "market-grid" },
- filtered.length > 0
- ? filtered.map((item, index) =>
- div({ class: "market-item" },
- div({ class: "market-card left-col" },
- form({ method: "GET", action: `/market/${encodeURIComponent(item.id)}` },
- button({ class: "filter-btn", type: "submit" }, i18n.viewDetails)
- ),
- h2({ class: "market-card type" }, `${i18n.marketItemType}: ${item.item_type.toUpperCase()}`),
- h2(item.title),
- renderCardField(`${i18n.marketItemStatus}:`, item.status),
- item.deadline ? renderCardField(`${i18n.marketItemAvailable}:`, moment(item.deadline).format('YYYY/MM/DD HH:mm:ss')) : null,
- br, br,
- div({ class: "market-card image" },
- item.image
- ? img({ src: `/blob/${encodeURIComponent(item.image)}` })
- : img({ src: '/assets/images/default-market.png', alt: item.title })
- ),
- p(...renderUrl(item.description)),
- item.tags && item.tags.filter(Boolean).length
- ? div({ class: 'card-tags' }, item.tags.filter(Boolean).map(tag =>
- a({ class: "tag-link", href: `/search?query=%23${encodeURIComponent(tag)}` },
- `#${tag}`)
- ))
- : null,
- ),
- div({ class: "market-card right-col" },
- renderCardField(`${i18n.marketItemStock}:`, item.stock > 0 ? item.stock : i18n.marketOutOfStock),
- div({ class: "market-card price" },
- renderCardField(`${i18n.marketItemPrice}:`, `${item.price} ECO`),
- ),
- renderCardField(`${i18n.marketItemCondition}:`, item.item_status),
- renderCardField(`${i18n.marketItemIncludesShipping}:`, item.includesShipping ? i18n.YESLabel : i18n.NOLabel),
- renderCardField(`${i18n.marketItemSeller}:`),
- div({ class: "market-card image" },
- div({ class: 'card-field' },
- a({ class: 'user-link', href: `/author/${encodeURIComponent(item.seller)}` }, item.seller)
- )),
- item.item_type === 'auction' && item.auctions_poll.length > 0
- ? div({ class: "auction-info" },
- p({ class: "auction-bid-text" }, i18n.marketAuctionBids),
- table({ class: 'auction-bid-table' },
- tr(
- th(i18n.marketAuctionBidTime),
- th(i18n.marketAuctionUser),
- th(i18n.marketAuctionBidAmount)
- ),
- item.auctions_poll.map(bid => {
- const [userId, bidAmount, bidTime] = bid.split(':');
- return tr(
- td(moment(bidTime).format('YYYY-MM-DD HH:mm:ss')),
- td(a({ href: `/author/${encodeURIComponent(userId)}` }, userId)),
- td(`${parseFloat(bidAmount).toFixed(6)} ECO`)
- );
- })
- )
- )
- : null,
- div({ class: "market-card buttons" },
- (item.seller === userId) ? [
- form({ method: "POST", action: `/market/delete/${encodeURIComponent(item.id)}` },
- button({ class: "delete-btn", type: "submit" }, i18n.marketActionsDelete)
- ),
- (item.status !== 'SOLD' && item.status !== 'DISCARDED' && item.auctions_poll.length === 0)
- ? form({ method: "GET", action: `/market/edit/${encodeURIComponent(item.id)}` },
- button({ class: "update-btn", type: "submit" }, i18n.marketActionsUpdate)
- )
- : null,
- (item.status === 'FOR SALE')
- ? form({ method: "POST", action: `/market/sold/${encodeURIComponent(item.id)}` },
- button({ class: "sold-btn", type: "submit" }, i18n.marketActionsSold)
- )
- : null
- ] : [
- (item.status !== 'SOLD' && item.status !== 'DISCARDED' && item.item_type === 'auction')
- ? form({ method: "POST", action: `/market/bid/${encodeURIComponent(item.id)}` },
- input({ type: "number", name: "bidAmount", step: "0.000001", min: "0.000001", placeholder: i18n.marketYourBid, required: true }),
- br,
- button({ class: "buy-btn", type: "submit" }, i18n.marketPlaceBidButton)
- )
- : null,
- (item.status === 'FOR SALE' && item.item_type !== 'auction' && item.seller !== userId)
- ? form({ method: "POST", action: `/market/buy/${encodeURIComponent(item.id)}` },
- input({ type: "hidden", name: "buyerId", value: userId }),
- button({ class: "buy-btn", type: "submit" }, i18n.marketActionsBuy)
- )
- : null
- ]
- )
- )
- )
- )
- : p(i18n.marketNoItems)
- )
- )
- )
- );
- };
- exports.singleMarketView = async (item, filter) => {
- return template(
- item.title,
- section(
- div({ class: "filters" },
- form({ method: 'GET', action: '/market' },
- button({ type: 'submit', name: 'filter', value: 'all', class: filter === 'all' ? 'filter-btn active' : 'filter-btn' }, i18n.marketFilterAll),
- button({ type: 'submit', name: 'filter', value: 'mine', class: filter === 'mine' ? 'filter-btn active' : 'filter-btn' }, i18n.marketFilterMine),
- button({ type: 'submit', name: 'filter', value: 'exchange', class: filter === 'exchange' ? 'filter-btn active' : 'filter-btn' }, i18n.marketFilterItems),
- button({ type: 'submit', name: 'filter', value: 'auctions', class: filter === 'auctions' ? 'filter-btn active' : 'filter-btn' }, i18n.marketFilterAuctions),
- button({ type: 'submit', name: 'filter', value: 'new', class: filter === 'new' ? 'filter-btn active' : 'filter-btn' }, i18n.marketFilterNew),
- button({ type: 'submit', name: 'filter', value: 'used', class: filter === 'used' ? 'filter-btn active' : 'filter-btn' }, i18n.marketFilterUsed),
- button({ type: 'submit', name: 'filter', value: 'broken', class: filter === 'broken' ? 'filter-btn active' : 'filter-btn' }, i18n.marketFilterBroken),
- button({ type: 'submit', name: 'filter', value: 'for sale', class: filter === 'for sale' ? 'filter-btn active' : 'filter-btn' }, i18n.marketFilterForSale),
- button({ type: 'submit', name: 'filter', value: 'sold', class: filter === 'sold' ? 'filter-btn active' : 'filter-btn' }, i18n.marketFilterSold),
- button({ type: 'submit', name: 'filter', value: 'discarded', class: filter === 'discarded' ? 'filter-btn active' : 'filter-btn' }, i18n.marketFilterDiscarded),
- button({ type: 'submit', name: 'filter', value: 'recent', class: filter === 'recent' ? 'filter-btn active' : 'filter-btn' }, i18n.marketFilterRecent),
- button({ type: 'submit', name: 'filter', value: 'create', class: "create-button" }, i18n.marketCreateButton)
- )
- ),
- div({ class: "tags-header" },
- h2(item.title),
- renderCardField(`${i18n.marketItemType}:`, `${item.item_type.toUpperCase()}`),
- renderCardField(`${i18n.marketItemStatus}:`, item.status),
- renderCardField(`${i18n.marketItemCondition}:`, item.item_status),
- br,
- div({ class: "market-item image" },
- item.image
- ? img({ src: `/blob/${encodeURIComponent(item.image)}` })
- : img({ src: '/assets/images/default-market.png', alt: item.title })
- ),
- renderCardField(`${i18n.marketItemDescription}:`),
- p(...renderUrl(item.description)),
- item.tags && item.tags.length
- ? div({ class: 'card-tags' },
- item.tags.map(tag =>
- a({ href: `/search?query=%23${encodeURIComponent(tag)}`, class: "tag-link" }, `#${tag}`)
- )
- )
- : null,
- br,
- renderCardField(`${i18n.marketItemPrice}:`),
- br,
- div({ class: 'card-label' },
- h2(`${item.price} ECO`),
- ),
- br,
- renderCardField(`${i18n.marketItemStock}:`, item.stock > 0 ? item.stock : i18n.marketOutOfStock),
- renderCardField(`${i18n.marketItemIncludesShipping}:`, `${item.includesShipping ? i18n.YESLabel : i18n.NOLabel}`),
- item.deadline ? renderCardField(`${i18n.marketItemAvailable}:`, `${moment(item.deadline).format('YYYY/MM/DD HH:mm:ss')}`) : null,
- renderCardField(`${i18n.marketItemSeller}:`),
- br,
- div({ class: 'card-field' },
- a({ class: 'user-link', href: `/author/${encodeURIComponent(item.seller)}` }, item.seller)
- )
- ),
- item.item_type === 'auction'
- ? div({ class: "auction-info" },
- p({ class: "auction-bid-text" }, i18n.marketAuctionBids),
- table({ class: 'auction-bid-table' },
- tr(
- th(i18n.marketAuctionBidTime),
- th(i18n.marketAuctionUser),
- th(i18n.marketAuctionBidAmount)
- ),
- item.auctions_poll.map(bid => {
- const [userId, bidAmount, bidTime] = bid.split(':');
- return tr(
- td(moment(bidTime).format('YYYY-MM-DD HH:mm:ss')),
- td(a({ href: `/author/${encodeURIComponent(userId)}` }, userId)),
- td(`${parseFloat(bidAmount).toFixed(6)} ECO`)
- );
- })
- ),
- item.status !== 'SOLD' && item.status !== 'DISCARDED'
- ? form({ method: "POST", action: `/market/bid/${encodeURIComponent(item.id)}` },
- input({ type: "number", name: "bidAmount", step: "0.000001", min: "0.000001", placeholder: i18n.marketYourBid, required: true }),
- br(),
- button({ class: "buy-btn", type: "submit" }, i18n.marketPlaceBidButton)
- )
- : null
- )
- : null,
- div({ class: "market-item actions" },
- (item.seller === userId && item.status !== 'SOLD' && item.status !== 'DISCARDED' && item.item_type !== 'auction') ? [
- form({ method: "POST", action: `/market/delete/${encodeURIComponent(item.id)}` },
- button({ class: "delete-btn", type: "submit" }, i18n.marketActionsDelete)
- ),
- form({ method: "GET", action: `/market/edit/${encodeURIComponent(item.id)}` },
- button({ class: "update-btn", type: "submit" }, i18n.marketActionsUpdate)
- ),
- (item.status === 'FOR SALE')
- ? form({ method: "POST", action: `/market/sold/${encodeURIComponent(item.id)}` },
- button({ class: "sold-btn", type: "submit" }, i18n.marketActionsSold)
- )
- : null
- ] : null,
- (item.status === 'FOR SALE' && item.item_type !== 'auction' && item.seller !== userId)
- ? form({ method: "POST", action: `/market/buy/${encodeURIComponent(item.id)}` },
- button({ class: "buy-btn", type: "submit" }, i18n.marketActionsBuy)
- )
- : null
- )
- )
- );
- };
|