{{ formatPrice(selectedListing) }}
{{ label('status', selectedListing.status) }}{{ relativeDate(selectedListing.created_at) }}
{{ selectedListing.title }}
@@ -665,12 +701,74 @@ onMounted(async () => {
{{ phone.t(editing ? 'Apps.citymarkt.editListing' : 'Apps.citymarkt.createListing') }}{{ phone.t('Apps.citymarkt.step', { current: String(sellStep), total: '4' }) }}
-
{{ phone.t('Apps.citymarkt.addPhotos') }}
{{ phone.t('Apps.citymarkt.addPhotosBody') }}
+
+
+ {{ phone.t('Apps.citymarkt.addPhotos') }}
+ {{ phone.t('Apps.citymarkt.addPhotosBody') }}
+
+
+
+
+
+ {{ phone.t('Apps.citymarkt.selectedPhotos') }}
+ {{ selectedPhotoIds.length }} / 6
+
+
+
+
+
+
{{ phone.t('Apps.citymarkt.describeOffer') }}
{{ phone.t('Apps.citymarkt.priceAndPlace') }}
-
{{ phone.t('Apps.citymarkt.preview') }}
{{ formatPrice({ price: draft.price, price_type: draft.priceType }) }}{{ draft.title }}
{{ draft.description }}
{{ label('districts', draft.district) }}
+
{{ phone.t('Apps.citymarkt.preview') }}
{{ formatPrice({ price: draft.price, price_type: draft.priceType }) }}{{ draft.title }}
{{ draft.description }}
{{ label('districts', draft.district) }}
+
+
+
+
+
+
+
+
+
+
+
{{ phone.t('Apps.citymarkt.cameraHint') }}
+
+
+
@@ -755,4 +853,8 @@ onMounted(async () => {
.citymarkt__offer-panel header{display:flex;align-items:flex-start;justify-content:space-between;gap:8px}.citymarkt__offer-panel header div{min-width:0}.citymarkt__offer-panel header small,.citymarkt__offer-panel header strong{display:block}.citymarkt__offer-panel header small{color:var(--yellow);font-size:8px;font-weight:900;text-transform:uppercase}.citymarkt__offer-panel header strong{overflow:hidden;font-size:12px;white-space:nowrap;text-overflow:ellipsis}.citymarkt__offer-panel header button{width:27px;height:27px;flex:none;padding:0;border:0;border-radius:9px;display:grid;place-items:center;background:#ffffff0b}.citymarkt__offer-panel label{margin-top:9px;display:block;color:var(--muted);font-size:8px;font-weight:800}.citymarkt__offer-panel label>span{height:39px;margin-top:4px;padding:0 10px;border:1px solid #ffffff16;border-radius:11px;display:flex;align-items:center;gap:5px;background:#151613}.citymarkt__offer-panel label b{color:var(--yellow);font-size:16px}.citymarkt__offer-panel input{min-width:0;flex:1;border:0;outline:0;background:none;color:inherit;font-size:16px;font-weight:900}.citymarkt__offer-panel>button{width:100%;min-height:36px;margin-top:8px;border:0;border-radius:11px;display:flex;align-items:center;justify-content:center;gap:5px;background:var(--yellow);color:#171816;font-size:9px;font-weight:900}.citymarkt__offer-panel>button:disabled{opacity:.45}
:global(.citymarkt--light) .citymarkt__offer-panel{background:#fff;box-shadow:0 14px 35px #0003}:global(.citymarkt--light) .citymarkt__offer-panel label>span{border-color:#00000012;background:#f4f4ef}
.citymarkt__form-select{margin-top:5px}
+.citymarkt__card-image--empty{background:linear-gradient(145deg,#2b2d28,#1e201d)!important}.citymarkt__image-placeholder{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;color:var(--muted)}.citymarkt__image-placeholder small{font-size:7px;font-weight:700}.citymarkt__thumb--empty{display:grid!important;place-items:center;background:linear-gradient(145deg,#2b2d28,#1e201d)!important;color:var(--muted)}
+.citymarkt__photo-actions{display:grid;grid-template-columns:1fr 1fr;gap:8px}.citymarkt__photo-actions>button{min-width:0;padding:11px 9px;border:1px solid #ffffff12;border-radius:14px;display:flex;flex-direction:column;align-items:flex-start;text-align:left;background:var(--panel)}.citymarkt__photo-actions>button>span{width:34px;height:34px;margin-bottom:8px;border-radius:11px;display:grid;place-items:center;background:#3d3621;color:var(--yellow)}.citymarkt__photo-actions strong{font-size:10px}.citymarkt__photo-actions small{margin-top:2px;color:var(--muted);font-size:7px;line-height:1.35}.citymarkt__selected-heading{margin:15px 1px 7px;display:flex;align-items:center;justify-content:space-between}.citymarkt__selected-heading strong{font-size:11px}.citymarkt__selected-heading span{padding:3px 6px;border-radius:7px;background:var(--panel);color:var(--yellow);font-size:8px;font-weight:900}.citymarkt__selection-gallery{height:142px;border-radius:14px}.citymarkt__selected-strip{margin-top:7px;display:flex;gap:6px;overflow-x:auto;scrollbar-width:none}.citymarkt__selected-strip button{position:relative;width:46px;height:46px;flex:none;border:1px solid #ffffff1d;border-radius:9px;background-position:center!important;background-size:cover!important}.citymarkt__selected-strip button i{position:absolute;left:3px;bottom:3px;width:15px;height:15px;border-radius:50%;display:grid;place-items:center;background:var(--yellow);color:#171816;font-size:7px;font-style:normal;font-weight:900}.citymarkt__selected-strip button svg{position:absolute;top:3px;right:3px;padding:2px;box-sizing:content-box;border-radius:50%;background:#11120fc7;color:#fff}
+.citymarkt__photo-source{position:absolute;z-index:8;inset:46px 0 0;padding:14px 14px 33px;background:#151613}.citymarkt--light .citymarkt__photo-source{background:#fafaf7}.citymarkt__photo-source>header{height:52px;display:flex;align-items:center;gap:8px}.citymarkt__photo-source>header>div{min-width:0;flex:1}.citymarkt__photo-source>header small,.citymarkt__photo-source>header strong{display:block}.citymarkt__photo-source>header small{color:var(--yellow);font-size:8px;font-weight:900;text-transform:uppercase}.citymarkt__photo-source>header strong{font-size:18px}.citymarkt__photo-source>header>span{padding:4px 7px;border-radius:8px;background:var(--panel);color:var(--yellow);font-size:8px;font-weight:900}.citymarkt__photo-source>header>button{width:31px;height:31px;padding:0;border:0;border-radius:50%;display:grid;place-items:center;background:var(--panel)}.citymarkt__photo-source>.citymarkt__photo-picker{max-height:calc(100% - 58px);padding-bottom:12px;overflow-y:auto;scrollbar-width:none}.citymarkt__photo-source .citymarkt__photo-picker button{position:relative;background-position:center!important}.citymarkt__capture{height:calc(100% - 52px);display:flex;flex-direction:column;align-items:center}.citymarkt__viewfinder{position:relative;width:100%;min-height:305px;overflow:hidden;border-radius:18px;background-position:center!important;background-size:cover!important;box-shadow:inset 0 0 0 1px #ffffff1c}.citymarkt__viewfinder:after{content:'';position:absolute;inset:0;background:linear-gradient(180deg,#0001,#00000038)}.citymarkt__viewfinder>i{position:absolute;z-index:2;width:25px;height:25px;border-color:#fff;border-style:solid}.citymarkt__viewfinder .corner-tl{top:18px;left:18px;border-width:2px 0 0 2px}.citymarkt__viewfinder .corner-tr{top:18px;right:18px;border-width:2px 2px 0 0}.citymarkt__viewfinder .corner-bl{bottom:18px;left:18px;border-width:0 0 2px 2px}.citymarkt__viewfinder .corner-br{right:18px;bottom:18px;border-width:0 2px 2px 0}.citymarkt__camera-flash{position:absolute;z-index:4;inset:0;background:#fff;opacity:0;pointer-events:none;transition:opacity .12s}.citymarkt__camera-flash.active{opacity:.9}.citymarkt__capture p{max-width:230px;margin:9px 0;color:var(--muted);font-size:8px;text-align:center}.citymarkt__shutter{width:58px;height:58px;padding:0;border:5px solid #f5f5ee;border-radius:50%;display:grid;place-items:center;background:var(--yellow);color:#171816;box-shadow:0 0 0 2px #ffffff42}.citymarkt__preview-image{overflow:hidden}
+:global(.citymarkt--light) .citymarkt__card-image--empty,:global(.citymarkt--light) .citymarkt__thumb--empty{background:linear-gradient(145deg,#ecece7,#dedfd8)!important}:global(.citymarkt--light) .citymarkt__photo-actions>button{border-color:#00000010}:global(.citymarkt--light) .citymarkt__selected-strip button{border-color:#00000018}
diff --git a/frontend/testserver/index.cjs b/frontend/testserver/index.cjs
index 655a5f9..978f18a 100644
--- a/frontend/testserver/index.cjs
+++ b/frontend/testserver/index.cjs
@@ -81,7 +81,11 @@ const marketplaceListings = [
district: 'vinewood', status: 'active', revision: 1, show_phone: 0, phone_number: null,
created_at: '2026-08-06 09:20:00', updated_at: '2026-08-06 09:20:00', expires_at: '2026-08-13 09:20:00',
image: 'linear-gradient(145deg, #ff6b6b, #845ec2 52%, #0f2027)', is_favorite: false,
- images: [{ media_id: 'capture-car', gradient: 'linear-gradient(145deg, #ff6b6b, #845ec2 52%, #0f2027)', sort_order: 1 }],
+ images: [
+ { media_id: 'capture-car', gradient: 'linear-gradient(145deg, #ff6b6b, #845ec2 52%, #0f2027)', sort_order: 1 },
+ { media_id: 'city-lights', gradient: 'linear-gradient(135deg, #fbc2eb, #a6c1ee 48%, #302b63)', sort_order: 2 },
+ { media_id: 'ocean-air', gradient: 'linear-gradient(160deg, #67d5b5, #26648e 55%, #0b132b)', sort_order: 3 },
+ ],
},
{
id: '81bc9d37-20e1-4d8a-82f8-f4b85f77cf02', seller_account_id: 3, seller_name: 'jamie',
@@ -90,8 +94,8 @@ const marketplaceListings = [
category: 'property', item_condition: 'very_good', price_type: 'fixed', price: 420000,
district: 'vespucci', status: 'active', revision: 1, show_phone: 0, phone_number: null,
created_at: '2026-08-05 17:10:00', updated_at: '2026-08-05 17:10:00', expires_at: '2026-08-12 17:10:00',
- image: 'linear-gradient(150deg, #f6d365, #fda085 45%, #512b58)', is_favorite: true,
- images: [{ media_id: 'desert-road', gradient: 'linear-gradient(150deg, #f6d365, #fda085 45%, #512b58)', sort_order: 1 }],
+ image: null, is_favorite: true,
+ images: [],
},
{
id: '81bc9d37-20e1-4d8a-82f8-f4b85f77cf03', seller_account_id: 4, seller_name: 'citytech',
@@ -370,7 +374,7 @@ app.post('/api/:endpoint', (request, response) => {
const photo = { 'sunset-drive': 'linear-gradient(145deg, #ff9a62, #5f2c82 58%, #141e30)', 'ocean-air': 'linear-gradient(160deg, #67d5b5, #26648e 55%, #0b132b)', 'city-lights': 'linear-gradient(135deg, #fbc2eb, #a6c1ee 48%, #302b63)', 'desert-road': 'linear-gradient(150deg, #f6d365, #fda085 45%, #512b58)' }[image.id] ?? 'linear-gradient(145deg, #ff6b6b, #845ec2 52%, #0f2027)'
return { media_id: image.id, gradient: photo, sort_order: 1 }
})
- marketplaceListings.unshift({ ...request.body, id, seller_account_id: 1, seller_name: 'demo', seller_since: '2026-08-04 12:00:00', seller_active: 1, item_condition: request.body.condition, price_type: request.body.priceType, show_phone: request.body.showPhone ? 1 : 0, phone_number: null, status: 'active', revision: 1, created_at: '2026-08-06 11:00:00', updated_at: '2026-08-06 11:00:00', expires_at: '2026-08-13 11:00:00', images: selected, image: selected[0]?.gradient, is_favorite: false })
+ marketplaceListings.unshift({ ...request.body, id, seller_account_id: 1, seller_name: 'demo', seller_since: '2026-08-04 12:00:00', seller_active: 1, item_condition: request.body.condition, price_type: request.body.priceType, show_phone: request.body.showPhone ? 1 : 0, phone_number: null, status: 'active', revision: 1, created_at: '2026-08-06 11:00:00', updated_at: '2026-08-06 11:00:00', expires_at: '2026-08-13 11:00:00', images: selected, image: selected[0]?.gradient ?? null, is_favorite: false })
response.json({ success: true, data: { id } })
return
}
@@ -386,7 +390,7 @@ app.post('/api/:endpoint', (request, response) => {
sort_order: index + 1,
}))
Object.assign(item, request.body, {
- image: selected[0]?.gradient,
+ image: selected[0]?.gradient ?? null,
images: selected,
item_condition: request.body.condition,
price_type: request.body.priceType,
diff --git a/sky_phone/source/html/index.html b/sky_phone/source/html/index.html
index 609abfe..59780c3 100644
--- a/sky_phone/source/html/index.html
+++ b/sky_phone/source/html/index.html
@@ -4,7 +4,7 @@
Sky Phone
-
+
diff --git a/sky_phone/source/server/marketplace.lua b/sky_phone/source/server/marketplace.lua
index 39ed568..420ae94 100644
--- a/sky_phone/source/server/marketplace.lua
+++ b/sky_phone/source/server/marketplace.lua
@@ -97,9 +97,12 @@ local function load_images(listing_id)
end
local function validate_images(source, imei, images)
- if type(images) ~= "table" or #images < 1 or #images > Config.Marketplace.MaxImages then
+ if type(images) ~= "table" or #images > Config.Marketplace.MaxImages then
return nil
end
+ if #images == 0 then
+ return {}
+ end
local owned_media = {
["sunset-drive"] = Config.Marketplace.PhotoGradients[1],