upstream: integrar 0.9.5 en style.css y las vistas grandes
style.css pasa a 0.9.5: lo unico que la rama le habia hecho era borrar reglas de housing, y su estilo propio vive en OasisMobile.css, que carga despues y gana igual. Se adopta 0.9.5 en tags_view (anade busqueda y filtros mine/recent), vote_view, market_view y activity_view, donde la rama solo arrastraba la version anterior: renderContentActions de dos parametros, comentarios de mercado propios y un import muerto de renderHiveNav que no se llegaba a llamar. Quedan a proposito en la version de la rama forum_view y chats_view: llevan funcionalidad movil propia (la tira de temas del foro y la interfaz de responder mensajes, que acompana al replyTo del modelo) entrelazada con estructuras que 0.9.5 ha reescrito. Integrarlas pide rehacer esa funcionalidad sobre la forma nueva, y no es un cambio que convenga colar entre otros veinte. Verificado con la app en marcha: 21 rutas OK, 0 claves i18n rotas, 0 requires rotos, interfaz intacta y orden de hojas correcto (style.css, mobile.css, OasisMobile.css).
This commit is contained in:
parent
373fba5d96
commit
7eeac8b004
5 changed files with 1722 additions and 643 deletions
|
|
@ -1,5 +1,7 @@
|
|||
const { div, h2, p, section, button, form, a, span, textarea, br, input, label, select, option, img, table, tr, th, td, progress, video, audio } = require("../server/node_modules/hyperaxe")
|
||||
const { template, i18n, userLink, renderStateChip, renderVisibilityChip, renderLifespanChip, renderEcoTax, renderSpreadButton } = require("./main_views")
|
||||
const { renderCommentsSection: renderSharedCommentsSection } = require("./comments_view");
|
||||
const { template, i18n, userLink, renderStateChip, renderVisibilityChip, renderLifespanChip, renderEcoTax, renderSpreadButton, renderSpreadEditWarning, renderOpinionsVoting, renderEngagement , renderContentActions } = require("./main_views")
|
||||
const opinionCategories = require("../backend/opinion_categories")
|
||||
const moment = require("../server/node_modules/moment")
|
||||
const { config } = require("../server/SSB_server.js")
|
||||
const { renderUrl } = require("../backend/renderUrl")
|
||||
|
|
@ -95,6 +97,17 @@ const buildReturnTo = (filter, q, minPrice, maxPrice, sort) => {
|
|||
return `/market${params.length ? `?${params.join("&")}` : ""}`
|
||||
}
|
||||
|
||||
|
||||
const sumCats = (opinions = {}, cats = []) => (cats || []).reduce((acc, c) => acc + (Number((opinions || {})[c]) || 0), 0)
|
||||
|
||||
const renderStarRating = (opinions, voterCount) => {
|
||||
const pos = sumCats(opinions, opinionCategories.positive)
|
||||
const neg = sumCats(opinions, opinionCategories.constructive) + sumCats(opinions, opinionCategories.moderation)
|
||||
const total = pos + neg
|
||||
const full = total > 0 ? Math.round((pos / total) * 5) : 0
|
||||
return span({ class: "shop-product-stars" }, "★".repeat(full) + "☆".repeat(5 - full) + ` (${voterCount})`)
|
||||
}
|
||||
|
||||
const renderCardField = (labelText, value = "") =>
|
||||
div({ class: "card-field" }, span({ class: "card-label" }, labelText), span({ class: "card-value" }, ...renderUrl(String(value))))
|
||||
|
||||
|
|
@ -121,59 +134,12 @@ const renderStockBar = (stockValue, maxValue) => {
|
|||
}
|
||||
|
||||
const renderMarketCommentsSection = (itemId, returnTo, comments = []) => {
|
||||
const commentsCount = Array.isArray(comments) ? comments.length : 0
|
||||
|
||||
return div(
|
||||
{ class: "vote-comments-section market-comments" },
|
||||
div({ class: "comments-count" }, span({ class: "card-label" }, i18n.voteCommentsLabel + ": "), span({ class: "card-value" }, String(commentsCount))),
|
||||
div(
|
||||
{ class: "comment-form-wrapper" },
|
||||
h2({ class: "comment-form-title" }, i18n.voteNewCommentLabel),
|
||||
form(
|
||||
{ method: "POST", action: `/market/${encodeURIComponent(itemId)}/comments`, class: "comment-form", enctype: "multipart/form-data" },
|
||||
input({ type: "hidden", name: "returnTo", value: returnTo }),
|
||||
textarea({ id: "comment-text", name: "text", rows: 4, class: "comment-textarea", placeholder: i18n.voteNewCommentPlaceholder }),
|
||||
div({ class: "comment-file-upload" }, label(i18n.uploadMedia), input({ type: "file", name: "blob" })),
|
||||
br(),
|
||||
button({ type: "submit", class: "comment-submit-btn" }, i18n.voteNewCommentButton)
|
||||
)
|
||||
),
|
||||
(() => {
|
||||
const visibleComments = (comments || []).filter(c => {
|
||||
const t = c && c.value && c.value.content && c.value.content.text
|
||||
return t && String(t).trim()
|
||||
})
|
||||
return visibleComments.length
|
||||
? div(
|
||||
{ class: "comments-list" },
|
||||
visibleComments.map((c) => {
|
||||
const author = c.value && c.value.author ? c.value.author : ""
|
||||
const ts = c.value && c.value.timestamp ? c.value.timestamp : c.timestamp
|
||||
const absDate = ts ? moment(ts).format("YYYY/MM/DD HH:mm:ss") : ""
|
||||
const relDate = ts ? moment(ts).fromNow() : ""
|
||||
const userName = author && author.includes("@") ? author.split("@")[1] : author
|
||||
const rootId = c.value && c.value.content ? c.value.content.fork || c.value.content.root : null
|
||||
const text = c.value && c.value.content && c.value.content.text ? c.value.content.text : ""
|
||||
|
||||
return div(
|
||||
{ class: "votations-comment-card" },
|
||||
span(
|
||||
{ class: "created-at" },
|
||||
span(i18n.createdBy),
|
||||
author ? userLink(author) : span("(unknown)"),
|
||||
absDate ? span(" | ") : "",
|
||||
absDate ? span({ class: "votations-comment-date" }, absDate) : "",
|
||||
relDate ? span({ class: "votations-comment-date" }, " | ", i18n.sendTime) : "",
|
||||
relDate && rootId ? a({ href: `/thread/${encodeURIComponent(rootId)}#${encodeURIComponent(c.key)}` }, relDate) : ""
|
||||
),
|
||||
p({ class: "votations-comment-text" }, ...renderUrl(text))
|
||||
)
|
||||
})
|
||||
)
|
||||
: p({ class: "votations-no-comments" }, i18n.voteNoCommentsYet)
|
||||
})()
|
||||
)
|
||||
}
|
||||
return renderSharedCommentsSection({
|
||||
action: `/market/${encodeURIComponent(itemId)}/comments`,
|
||||
comments: comments,
|
||||
returnTo: returnTo
|
||||
});
|
||||
};
|
||||
|
||||
const isMyBidItem = (item) => {
|
||||
const polls = Array.isArray(item.auctions_poll) ? item.auctions_poll : []
|
||||
|
|
@ -231,9 +197,9 @@ const renderMarketOwnerActions = (item, returnTo) => {
|
|||
input({ type: "hidden", name: "returnTo", value: returnTo }),
|
||||
select(
|
||||
{ name: "status", class: "project-control-select" },
|
||||
option({ value: "FOR SALE", selected: cur === "FOR SALE" }, i18n.marketFilterForSale),
|
||||
option({ value: "SOLD", selected: cur === "SOLD" }, i18n.marketFilterSold),
|
||||
option({ value: "DISCARDED", selected: cur === "DISCARDED" }, i18n.marketFilterDiscarded)
|
||||
option({ value: "FOR SALE", ...(cur === "FOR SALE" ? { selected: true } : {})}, i18n.marketFilterForSale),
|
||||
option({ value: "SOLD", ...(cur === "SOLD" ? { selected: true } : {})}, i18n.marketFilterSold),
|
||||
option({ value: "DISCARDED", ...(cur === "DISCARDED" ? { selected: true } : {})}, i18n.marketFilterDiscarded)
|
||||
),
|
||||
button({ class: "status-btn project-control-btn", type: "submit" }, i18n.marketActionsChangeStatus)
|
||||
)
|
||||
|
|
@ -260,21 +226,7 @@ exports.marketView = async (items, filter, itemToEdit = null, params = {}) => {
|
|||
const maxPrice = params.maxPrice
|
||||
const sort = params.sort || "recent"
|
||||
|
||||
let title = i18n.marketAllSectionTitle
|
||||
switch (filter) {
|
||||
case "mine":
|
||||
title = i18n.marketMineSectionTitle
|
||||
break
|
||||
case "create":
|
||||
title = i18n.marketCreateSectionTitle
|
||||
break
|
||||
case "edit":
|
||||
title = i18n.marketUpdateSectionTitle
|
||||
break
|
||||
case "mybids":
|
||||
title = i18n.marketFilterMyBids
|
||||
break
|
||||
}
|
||||
const title = i18n.marketTitle
|
||||
|
||||
let filtered = []
|
||||
switch (filter) {
|
||||
|
|
@ -337,6 +289,7 @@ exports.marketView = async (items, filter, itemToEdit = null, params = {}) => {
|
|||
]
|
||||
|
||||
const isFormMode = filter === "create" || filter === "edit"
|
||||
const spreadWarning = filter === "edit" ? await renderSpreadEditWarning(itemEdit && (itemEdit.id || itemEdit.key)) : null
|
||||
|
||||
return template(
|
||||
title,
|
||||
|
|
@ -347,18 +300,18 @@ exports.marketView = async (items, filter, itemToEdit = null, params = {}) => {
|
|||
form(
|
||||
{ method: "GET", action: "/market", class: "ui-toolbar ui-toolbar--filters" },
|
||||
...hiddenCtx,
|
||||
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: "mybids", class: filter === "mybids" ? "filter-btn active" : "filter-btn" }, i18n.marketFilterMyBids),
|
||||
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: "all", class: filter === "all" ? "filter-btn active" : "filter-btn" }, String(i18n.marketFilterAll).toUpperCase()),
|
||||
button({ type: "submit", name: "filter", value: "mine", class: filter === "mine" ? "filter-btn active" : "filter-btn" }, String(i18n.marketFilterMine).toUpperCase()),
|
||||
button({ type: "submit", name: "filter", value: "exchange", class: filter === "exchange" ? "filter-btn active" : "filter-btn" }, String(i18n.marketFilterItems).toUpperCase()),
|
||||
button({ type: "submit", name: "filter", value: "auctions", class: filter === "auctions" ? "filter-btn active" : "filter-btn" }, String(i18n.marketFilterAuctions).toUpperCase()),
|
||||
button({ type: "submit", name: "filter", value: "mybids", class: filter === "mybids" ? "filter-btn active" : "filter-btn" }, String(i18n.marketFilterMyBids).toUpperCase()),
|
||||
button({ type: "submit", name: "filter", value: "new", class: filter === "new" ? "filter-btn active" : "filter-btn" }, String(i18n.marketFilterNew).toUpperCase()),
|
||||
button({ type: "submit", name: "filter", value: "used", class: filter === "used" ? "filter-btn active" : "filter-btn" }, String(i18n.marketFilterUsed).toUpperCase()),
|
||||
button({ type: "submit", name: "filter", value: "broken", class: filter === "broken" ? "filter-btn active" : "filter-btn" }, String(i18n.marketFilterBroken).toUpperCase()),
|
||||
button({ type: "submit", name: "filter", value: "for sale", class: filter === "for sale" ? "filter-btn active" : "filter-btn" }, String(i18n.marketFilterForSale).toUpperCase()),
|
||||
button({ type: "submit", name: "filter", value: "sold", class: filter === "sold" ? "filter-btn active" : "filter-btn" }, String(i18n.marketFilterSold).toUpperCase()),
|
||||
button({ type: "submit", name: "filter", value: "discarded", class: filter === "discarded" ? "filter-btn active" : "filter-btn" }, String(i18n.marketFilterDiscarded).toUpperCase()),
|
||||
button({ type: "submit", name: "filter", value: "recent", class: filter === "recent" ? "filter-btn active" : "filter-btn" }, String(i18n.marketFilterRecent).toUpperCase()),
|
||||
button({ type: "submit", name: "filter", value: "create", class: "create-button" }, i18n.marketCreateButton)
|
||||
)
|
||||
),
|
||||
|
|
@ -394,9 +347,9 @@ exports.marketView = async (items, filter, itemToEdit = null, params = {}) => {
|
|||
),
|
||||
select(
|
||||
{ name: "sort", class: "filter-box__select" },
|
||||
option({ value: "recent", selected: sort === "recent" }, i18n.marketSortRecent),
|
||||
option({ value: "price", selected: sort === "price" }, i18n.marketSortPrice),
|
||||
option({ value: "deadline", selected: sort === "deadline" }, i18n.marketSortDeadline)
|
||||
option({ value: "recent", ...(sort === "recent" ? { selected: true } : {})}, i18n.marketSortRecent),
|
||||
option({ value: "price", ...(sort === "price" ? { selected: true } : {})}, i18n.marketSortPrice),
|
||||
option({ value: "deadline", ...(sort === "deadline" ? { selected: true } : {})}, i18n.marketSortDeadline)
|
||||
),
|
||||
button({ type: "submit", class: "filter-box__button" }, i18n.marketSearchButton)
|
||||
)
|
||||
|
|
@ -408,6 +361,7 @@ exports.marketView = async (items, filter, itemToEdit = null, params = {}) => {
|
|||
isFormMode
|
||||
? div(
|
||||
{ class: "market-form" },
|
||||
spreadWarning,
|
||||
form(
|
||||
{ action: filter === "edit" ? `/market/update/${encodeURIComponent(itemEdit.id)}` : "/market/create", method: "POST", enctype: "multipart/form-data" },
|
||||
input({ type: "hidden", name: "returnTo", value: "/market?filter=mine" }),
|
||||
|
|
@ -416,14 +370,14 @@ exports.marketView = async (items, filter, itemToEdit = null, params = {}) => {
|
|||
br(),
|
||||
select(
|
||||
{ name: "item_type", id: "item_type", required: true },
|
||||
option({ value: "auction", selected: itemEdit && itemEdit.item_type === "auction" }, "Auction"),
|
||||
option({ value: "exchange", selected: itemEdit && itemEdit.item_type === "exchange" }, "Exchange")
|
||||
option({ value: "auction", ...(itemEdit && itemEdit.item_type === "auction" ? { selected: true } : {})}, "Auction"),
|
||||
option({ value: "exchange", ...(itemEdit && itemEdit.item_type === "exchange" ? { selected: true } : {})}, "Exchange")
|
||||
),
|
||||
br(),
|
||||
br(),
|
||||
label(i18n.marketItemTitle),
|
||||
br(),
|
||||
input({ type: "text", name: "title", id: "title", value: (itemEdit && itemEdit.title) || params.title || "", required: true }),
|
||||
input({ type: "text", name: "title", maxlength: "100", id: "title", value: (itemEdit && itemEdit.title) || params.title || "", required: true }),
|
||||
br(),
|
||||
br(),
|
||||
label(i18n.marketItemDescription),
|
||||
|
|
@ -440,9 +394,9 @@ exports.marketView = async (items, filter, itemToEdit = null, params = {}) => {
|
|||
br(),
|
||||
select(
|
||||
{ name: "item_status", id: "item_status" },
|
||||
option({ value: "BROKEN", selected: itemEdit && itemEdit.item_status === "BROKEN" }, "BROKEN"),
|
||||
option({ value: "USED", selected: itemEdit && itemEdit.item_status === "USED" }, "USED"),
|
||||
option({ value: "NEW", selected: itemEdit && itemEdit.item_status === "NEW" }, "NEW")
|
||||
option({ value: "BROKEN", ...(itemEdit && itemEdit.item_status === "BROKEN" ? { selected: true } : {})}, "BROKEN"),
|
||||
option({ value: "USED", ...(itemEdit && itemEdit.item_status === "USED" ? { selected: true } : {})}, "USED"),
|
||||
option({ value: "NEW", ...(itemEdit && itemEdit.item_status === "NEW" ? { selected: true } : {})}, "NEW")
|
||||
),
|
||||
br(),
|
||||
br(),
|
||||
|
|
@ -460,8 +414,8 @@ exports.marketView = async (items, filter, itemToEdit = null, params = {}) => {
|
|||
br(),
|
||||
select(
|
||||
{ name: "visibility" },
|
||||
option({ value: "PUBLIC", selected: (itemEdit?.visibility || "PUBLIC") === "PUBLIC" }, i18n.visibilityPublic || "Public"),
|
||||
option({ value: "HIDDEN", selected: itemEdit?.visibility === "HIDDEN" }, i18n.visibilityHidden || "Hidden")
|
||||
option({ value: "PUBLIC", ...((itemEdit?.visibility || "PUBLIC") === "PUBLIC" ? { selected: true } : {})}, i18n.visibilityPublic || "Public"),
|
||||
option({ value: "HIDDEN", ...(itemEdit?.visibility === "HIDDEN" ? { selected: true } : {})}, i18n.visibilityHidden || "Hidden")
|
||||
),
|
||||
br(),
|
||||
br(),
|
||||
|
|
@ -541,10 +495,15 @@ exports.marketView = async (items, filter, itemToEdit = null, params = {}) => {
|
|||
renderMediaBlob(item.image, '/assets/images/default-market.png', { class: 'tribe-card-hero-image' })
|
||||
)
|
||||
),
|
||||
div({ class: "card-header activity-card-header" },
|
||||
span(),
|
||||
renderContentActions(item.id, `/market/${encodeURIComponent(item.id)}`, { spread: params.spreads || null, author: item.seller, favKind: 'market', isFavorite: item.isFavorite, reportTitle: item.title })
|
||||
),
|
||||
div({ class: "tribe-card-body" },
|
||||
div({ class: "shop-title-row" },
|
||||
h2({ class: "tribe-card-title" }, a({ href: `/market/${encodeURIComponent(item.id)}` }, item.title))
|
||||
),
|
||||
renderStarRating(item.opinions, Array.isArray(item.opinions_inhabitants) ? item.opinions_inhabitants.length : 0),
|
||||
div({ class: "card-chips-row" },
|
||||
renderStateChip("encrypted", "", String(item.item_type || "").toUpperCase()),
|
||||
item.item_status ? renderStateChip("whole", "", String(item.item_status).toUpperCase()) : null,
|
||||
|
|
@ -553,18 +512,7 @@ exports.marketView = async (items, filter, itemToEdit = null, params = {}) => {
|
|||
item.industry ? a({ href: `/industry/${encodeURIComponent(item.industry)}` }, renderStateChip("whole", "🏭", String(i18n.industryTitle || "Industry").toUpperCase())) : null,
|
||||
renderLifespanChip(item.lifetime, i18n)
|
||||
),
|
||||
div({ class: "market-card-price card-date-highlight" }, `${item.price} ECO`),
|
||||
div({ class: "card-visit-btn-centered" },
|
||||
form({ method: 'GET', action: `/market/${encodeURIComponent(item.id)}` },
|
||||
input({ type: "hidden", name: "returnTo", value: returnTo }),
|
||||
input({ type: "hidden", name: "filter", value: filter || "all" }),
|
||||
input({ type: "hidden", name: "q", value: q }),
|
||||
input({ type: "hidden", name: "minPrice", value: String(minPrice ?? "") }),
|
||||
input({ type: "hidden", name: "maxPrice", value: String(maxPrice ?? "") }),
|
||||
input({ type: "hidden", name: "sort", value: sort }),
|
||||
button({ type: 'submit', class: 'filter-btn' }, i18n.viewItem || i18n.marketVisitItem || 'View Item')
|
||||
)
|
||||
)
|
||||
div({ class: "market-card-price card-date-highlight" }, `${item.price} ECO`)
|
||||
)
|
||||
)
|
||||
})
|
||||
|
|
@ -589,6 +537,7 @@ exports.singleMarketView = async (item, filter, comments = [], params = {}) => {
|
|||
|
||||
return template(
|
||||
item.title,
|
||||
section(div({ class: "tags-header" }, h2(i18n.marketTitle), p(i18n.marketDescription))),
|
||||
section(
|
||||
div(
|
||||
{ class: "filters" },
|
||||
|
|
@ -598,18 +547,18 @@ exports.singleMarketView = async (item, filter, comments = [], params = {}) => {
|
|||
input({ type: "hidden", name: "minPrice", value: minPrice ?? "" }),
|
||||
input({ type: "hidden", name: "maxPrice", value: maxPrice ?? "" }),
|
||||
input({ type: "hidden", name: "sort", value: sort }),
|
||||
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: "mybids", class: filter === "mybids" ? "filter-btn active" : "filter-btn" }, i18n.marketFilterMyBids),
|
||||
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: "all", class: filter === "all" ? "filter-btn active" : "filter-btn" }, String(i18n.marketFilterAll).toUpperCase()),
|
||||
button({ type: "submit", name: "filter", value: "mine", class: filter === "mine" ? "filter-btn active" : "filter-btn" }, String(i18n.marketFilterMine).toUpperCase()),
|
||||
button({ type: "submit", name: "filter", value: "exchange", class: filter === "exchange" ? "filter-btn active" : "filter-btn" }, String(i18n.marketFilterItems).toUpperCase()),
|
||||
button({ type: "submit", name: "filter", value: "auctions", class: filter === "auctions" ? "filter-btn active" : "filter-btn" }, String(i18n.marketFilterAuctions).toUpperCase()),
|
||||
button({ type: "submit", name: "filter", value: "mybids", class: filter === "mybids" ? "filter-btn active" : "filter-btn" }, String(i18n.marketFilterMyBids).toUpperCase()),
|
||||
button({ type: "submit", name: "filter", value: "new", class: filter === "new" ? "filter-btn active" : "filter-btn" }, String(i18n.marketFilterNew).toUpperCase()),
|
||||
button({ type: "submit", name: "filter", value: "used", class: filter === "used" ? "filter-btn active" : "filter-btn" }, String(i18n.marketFilterUsed).toUpperCase()),
|
||||
button({ type: "submit", name: "filter", value: "broken", class: filter === "broken" ? "filter-btn active" : "filter-btn" }, String(i18n.marketFilterBroken).toUpperCase()),
|
||||
button({ type: "submit", name: "filter", value: "for sale", class: filter === "for sale" ? "filter-btn active" : "filter-btn" }, String(i18n.marketFilterForSale).toUpperCase()),
|
||||
button({ type: "submit", name: "filter", value: "sold", class: filter === "sold" ? "filter-btn active" : "filter-btn" }, String(i18n.marketFilterSold).toUpperCase()),
|
||||
button({ type: "submit", name: "filter", value: "discarded", class: filter === "discarded" ? "filter-btn active" : "filter-btn" }, String(i18n.marketFilterDiscarded).toUpperCase()),
|
||||
button({ type: "submit", name: "filter", value: "recent", class: filter === "recent" ? "filter-btn active" : "filter-btn" }, String(i18n.marketFilterRecent).toUpperCase()),
|
||||
button({ type: "submit", name: "filter", value: "create", class: "create-button" }, i18n.marketCreateButton)
|
||||
)
|
||||
),
|
||||
|
|
@ -637,26 +586,13 @@ exports.singleMarketView = async (item, filter, comments = [], params = {}) => {
|
|||
))
|
||||
pushRow(i18n.marketItemSeller, userLink(item.seller))
|
||||
if (item.shopId && item.shopTitle) pushRow(i18n.marketShopLabel || "Shop", a({ href: `/shops/${encodeURIComponent(item.shopId)}`, class: "user-link" }, item.shopTitle))
|
||||
if (item.deadline) pushRow(i18n.marketItemAvailable, moment(item.deadline).format("YYYY/MM/DD HH:mm:ss"))
|
||||
|
||||
const itemSide = div({ class: "tribe-side" },
|
||||
div({ class: "shop-title-row" },
|
||||
h2({ class: "tribe-card-title" }, item.title)
|
||||
),
|
||||
chips.length ? div({ class: "card-chips-row" }, ...chips) : null,
|
||||
div({ class: "card-spread-centered" }, renderSpreadButton(item.id, params.spreads)),
|
||||
renderMediaBlob(item.image, "/assets/images/default-market.png"),
|
||||
div({ class: "card-date-highlight" }, `${item.price} ECO`),
|
||||
renderStockBar(item.stock, maxStock),
|
||||
table({ class: "tribe-info-table jobs-info-table" }, ...infoRows),
|
||||
tagsNode
|
||||
)
|
||||
if (item.deadline) pushRow(i18n.marketItemAvailable, moment(item.deadline).format("YYYY/MM/DD HH:mm"))
|
||||
|
||||
const visibilityRow = String(item.seller) === String(userId)
|
||||
? (() => {
|
||||
const vis = isHidden ? 'HIDDEN' : 'PUBLIC'
|
||||
const next = vis === 'PUBLIC' ? 'HIDDEN' : 'PUBLIC'
|
||||
return div({ class: "tribe-side-actions" },
|
||||
return div({ class: "tribe-side-actions housing-status-row" },
|
||||
span({ class: "card-label" }, `${i18n.visibilityLabel || 'Visibility'}: `),
|
||||
renderVisibilityChip(vis, i18n),
|
||||
form({ method: "POST", action: `/market/visibility/${encodeURIComponent(item.id)}`, class: "inline-form" },
|
||||
|
|
@ -670,12 +606,6 @@ exports.singleMarketView = async (item, filter, comments = [], params = {}) => {
|
|||
: null
|
||||
|
||||
const marketActions = []
|
||||
if (item.seller && String(item.seller) !== String(userId)) {
|
||||
marketActions.push(form({ method: "GET", action: "/pm" },
|
||||
input({ type: "hidden", name: "recipients", value: item.seller }),
|
||||
button({ type: "submit", class: "filter-btn" }, i18n.privateMessage)
|
||||
))
|
||||
}
|
||||
if (String(item.seller) === String(userId)) {
|
||||
marketActions.push(form({ method: "GET", action: `/market/edit/${encodeURIComponent(item.id)}` },
|
||||
button({ type: "submit", class: "update-btn" }, i18n.marketUpdateButton || "Update")
|
||||
|
|
@ -684,9 +614,26 @@ exports.singleMarketView = async (item, filter, comments = [], params = {}) => {
|
|||
button({ type: "submit", class: "delete-btn" }, i18n.marketDeleteButton || "Delete")
|
||||
))
|
||||
}
|
||||
const itemMain = div({ class: "tribe-main" },
|
||||
marketActions.length ? div({ class: "tribe-side-actions" }, ...marketActions) : null,
|
||||
|
||||
const itemSide = div({ class: "tribe-side" },
|
||||
div({ class: "card-header activity-card-header" },
|
||||
renderContentActions(item.id, null, { spread: params.spreads || null, author: item.seller, favKind: 'market', isFavorite: item.isFavorite, reportTitle: item.title })
|
||||
),
|
||||
div({ class: "shop-title-row" },
|
||||
h2({ class: "tribe-card-title" }, item.title)
|
||||
),
|
||||
renderStarRating(item.opinions, Array.isArray(item.opinions_inhabitants) ? item.opinions_inhabitants.length : 0),
|
||||
chips.length ? div({ class: "card-chips-row" }, ...chips) : null,
|
||||
renderMediaBlob(item.image, "/assets/images/default-market.png"),
|
||||
div({ class: "card-date-highlight" }, `${item.price} ECO`),
|
||||
renderStockBar(item.stock, maxStock),
|
||||
table({ class: "tribe-info-table jobs-info-table" }, ...infoRows),
|
||||
tagsNode,
|
||||
visibilityRow,
|
||||
marketActions.length ? div({ class: "tribe-side-actions" }, ...marketActions) : null
|
||||
)
|
||||
|
||||
const itemMain = div({ class: "tribe-main" },
|
||||
item.description
|
||||
? div({ class: "job-section" },
|
||||
h2({ class: "job-section-title" }, i18n.marketItemDescription),
|
||||
|
|
@ -704,7 +651,7 @@ exports.singleMarketView = async (item, filter, comments = [], params = {}) => {
|
|||
{ class: "auction-bid-table" },
|
||||
tr(th(i18n.marketAuctionBidTime), th(i18n.marketAuctionUser), th(i18n.marketAuctionBidAmount)),
|
||||
parsedBids.map((bid) =>
|
||||
tr(td(moment(bid.time).format("YYYY-MM-DD HH:mm:ss")), td(a({ href: `/author/${encodeURIComponent(bid.bidder)}` }, bid.bidder)), td(`${parseFloat(bid.amount).toFixed(6)} ECO`))
|
||||
tr(td(moment(bid.time).format("YYYY/MM/DD HH:mm")), td(a({ href: `/author/${encodeURIComponent(bid.bidder)}` }, bid.bidder)), td(`${parseFloat(bid.amount).toFixed(6)} ECO`))
|
||||
)
|
||||
)
|
||||
: p({ class: "tribe-side-description" }, i18n.marketNoBids || "No bids yet"),
|
||||
|
|
@ -729,7 +676,12 @@ exports.singleMarketView = async (item, filter, comments = [], params = {}) => {
|
|||
)
|
||||
)
|
||||
: null,
|
||||
renderMarketCommentsSection(item.id, returnTo, comments)
|
||||
renderEngagement(item.id,
|
||||
String(item.seller) !== String(userId) && item.purchasedByViewer
|
||||
? renderOpinionsVoting("/market/opinions", item.id, item.opinions, returnTo, item.opinions_inhabitants)
|
||||
: null,
|
||||
renderMarketCommentsSection(item.id, returnTo, comments)
|
||||
)
|
||||
)
|
||||
|
||||
return div({ class: "tribe-details" }, itemSide, itemMain)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue