diff --git a/backend/app/db/fixmate.db b/backend/app/db/fixmate.db index 12fb3c6..40517cd 100644 Binary files a/backend/app/db/fixmate.db and b/backend/app/db/fixmate.db differ diff --git a/backend/static/uploads/ce032d6b-119b-401e-a4b2-6fa3aee7c1cc.jpg b/backend/static/uploads/ce032d6b-119b-401e-a4b2-6fa3aee7c1cc.jpg new file mode 100644 index 0000000..da1166b Binary files /dev/null and b/backend/static/uploads/ce032d6b-119b-401e-a4b2-6fa3aee7c1cc.jpg differ diff --git a/dashboard/app.js b/dashboard/app.js index b1f62a6..0f489c0 100644 --- a/dashboard/app.js +++ b/dashboard/app.js @@ -446,10 +446,10 @@ const cycleStatus = async (reportId) => {
{t('drawer.details') || 'Details'}
{selected.notes ?{selected.notes}
:{t('drawer.noNotes') || 'No additional notes'}
} -{t('label.submittedBy') || 'Submitted by'}: {selected.userName || (t('label.guest') || 'Guest')}
+{t('label.submittedBy') || 'Submitted by'}: {selected.userName && !selected.userName.startsWith('Guest-') ? selected.userName : (t('label.anonymous') || 'Anonymous User')}
{t('label.place') || 'Place'}: {selected.address ? selected.address : `${selected.location.lat.toFixed(5)}, ${selected.location.lng.toFixed(5)}`}
{t('label.location') || 'Location'}: {selected.location.lat.toFixed(5)}, {selected.location.lng.toFixed(5)}
{t('label.createdAt') || 'Created'}: {dayjs(selected.createdAt).format('YYYY-MM-DD HH:mm')}
diff --git a/dashboard/i18n/en.json b/dashboard/i18n/en.json index a33993e..39db4c7 100644 --- a/dashboard/i18n/en.json +++ b/dashboard/i18n/en.json @@ -13,6 +13,9 @@ "label.language": "Language", "label.location": "Location", "label.createdAt": "Created At", + "label.submittedBy": "Submitted by", + "label.place": "Place", + "label.anonymous": "Anonymous User", "filter.category": "Category", "filter.severity": "Severity", "filter.status": "Status", diff --git a/dashboard/i18n/ms.json b/dashboard/i18n/ms.json index 8cca55a..667ccc9 100644 --- a/dashboard/i18n/ms.json +++ b/dashboard/i18n/ms.json @@ -13,6 +13,9 @@ "label.language": "Bahasa", "label.location": "Lokasi", "label.createdAt": "Dicipta Pada", + "label.submittedBy": "Dihantar oleh", + "label.place": "Tempat", + "label.anonymous": "Pengguna Tanpa Nama", "filter.category": "Kategori", "filter.severity": "Keparahan", "filter.status": "Status", diff --git a/dashboard/styles.css b/dashboard/styles.css index 4019d6a..5e66cfb 100644 --- a/dashboard/styles.css +++ b/dashboard/styles.css @@ -30,29 +30,59 @@ body{ display:flex; align-items:center; justify-content:space-between; - padding:0 16px; + padding:0 20px; background:var(--panel); - border-bottom:1px solid #e6eef3; - box-shadow: none; + border-bottom:1px solid #e5e7eb; + box-shadow:0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); z-index:100; } -.brand{font-weight:700;font-size:18px;color:#111827} -.lang-toggle select{padding:6px;border-radius:6px;border:1px solid #e6eef3;background:white} +.brand{ + font-weight:700; + font-size:20px; + color:#111827; + background:linear-gradient(135deg, var(--accent), #0d9488); + -webkit-background-clip:text; + -webkit-text-fill-color:transparent; + background-clip:text; +} +.lang-toggle{ + display:flex; + align-items:center; + gap:8px; +} +.lang-toggle select{ + padding:8px 12px; + border:1px solid #d1d5db; + border-radius:6px; + background:white; + font-size:14px; + color:#374151; + cursor:pointer; + transition:border-color 0.2s ease; +} +.lang-toggle select:focus{ + outline:none; + border-color:var(--accent); + box-shadow:0 0 0 3px rgba(14,165,164,0.1); +} .app-root{height:100vh;display:flex;flex-direction:column} .container{ height:calc(100vh - 56px); display:flex; flex-direction:column; - gap:8px; - padding:12px; + gap:16px; + padding:16px; + max-width:1400px; + margin:0 auto; + width:100%; } /* main area */ .main{ display:grid; - grid-template-columns:300px 1fr 340px; + grid-template-columns:300px 1fr 400px; gap:12px; align-items:stretch; flex:1; @@ -61,35 +91,121 @@ body{ /* panels */ .panel{ background:var(--panel); - border-radius:8px; - box-shadow:var(--shadow); - padding:12px; + border-radius:12px; + box-shadow:0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); + padding:20px; display:flex; flex-direction:column; min-height:0; + border:1px solid rgba(0,0,0,0.05); } -.filters h3{margin:0 0 8px 0} -.filter-group{margin-bottom:12px} -.checkbox-row{display:flex;flex-direction:column;gap:6px;max-height:220px;overflow:auto;padding-right:6px} -.checkbox-row label{font-size:13px;color:#111827} +.filters h3{ + margin:0 0 20px 0; + font-size:18px; + font-weight:600; + color:#111827; + border-bottom:2px solid #f3f4f6; + padding-bottom:12px; +} +.filter-group{ + margin-bottom:24px; + padding:16px; + background:#fafbfc; + border-radius:8px; + border:1px solid #e5e7eb; + overflow:hidden; +} +.filter-group:last-child{margin-bottom:0;} +.checkbox-row{ + display:flex; + flex-direction:column; + gap:12px; + max-height:240px; + overflow-y:auto; + padding-right:8px; +} +.checkbox-row label{ + font-size:14px; + color:#374151; + font-weight:500; + display:flex; + align-items:center; + gap:10px; + padding:8px; + border-radius:6px; + transition:background-color 0.2s ease; + cursor:pointer; +} +.checkbox-row label:hover{ + background-color:#f3f4f6; +} /* chips/buttons */ .btn{ background:var(--accent); color:white; border:none; - padding:8px 12px; - border-radius:6px; + padding:10px 20px; + border-radius:8px; cursor:pointer; font-weight:600; + font-size:14px; + transition:all 0.2s ease; + box-shadow:0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); +} +.btn:hover{ + background:#0d9488; + transform:translateY(-1px); + box-shadow:0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); +} +.btn.secondary{ + background:#f8fafc; + color:#475569; + border:1px solid #e2e8f0; +} +.btn.secondary:hover{ + background:#f1f5f9; + border-color:#cbd5e1; +} +.btn.ghost{ + background:transparent; + border:1px solid #e2e8f0; + color:#475569; + padding:8px 16px; +} +.btn.ghost:hover{ + background:#f8fafc; + border-color:#cbd5e1; +} +.btn:focus{ + outline:2px solid rgba(14,165,164,0.25); + outline-offset:2px; } -.btn.secondary{background:#f1f5f9;color:#0f172a} -.btn.ghost{background:transparent;border:1px solid #e6eef3;color:#0f172a;padding:6px 10px} -.btn:focus{outline:2px solid rgba(14,165,164,0.25)} -.multi-select{display:flex;gap:8px;flex-wrap:wrap} -.chip{display:inline-block;padding:4px 8px;border-radius:14px;font-size:13px;color:white} +.multi-select{ + display:flex; + gap:10px; + flex-wrap:wrap; + margin-top:12px; +} +.chip{ + display:inline-flex; + align-items:center; + padding:6px 12px; + border-radius:20px; + font-size:13px; + font-weight:500; + color:white; + transition:all 0.2s ease; + cursor:pointer; + border:none; + box-shadow:0 1px 3px 0 rgba(0, 0, 0, 0.1); +} +.chip:hover{ + transform:translateY(-1px); + box-shadow:0 2px 4px 0 rgba(0, 0, 0, 0.15); +} .chip.severity-high{background:var(--severity-high)} .chip.severity-medium{background:var(--severity-medium)} .chip.severity-low{background:var(--severity-low)} @@ -98,8 +214,18 @@ body{ .chip.status-fixed{background:var(--status-fixed)} /* severity buttons in filter */ -button.chip{border:none;cursor:pointer;opacity:0.95} -button.chip[aria-pressed="false"]{opacity:0.55;filter:grayscale(0.15)} +button.chip{ + opacity:0.9; + transition:opacity 0.2s ease, filter 0.2s ease; +} +button.chip[aria-pressed="false"]{ + opacity:0.4; + filter:grayscale(0.3) saturate(0.5); +} +button.chip[aria-pressed="true"]{ + opacity:1; + filter:none; +} /* map panel */ .map-panel{position:relative;min-height:0;height:100%;padding:0;overflow:hidden} @@ -115,11 +241,11 @@ button.chip[aria-pressed="false"]{opacity:0.55;filter:grayscale(0.15)} .map-panel.no-reports .map-empty{display:flex} /* queue list */ -.queue-list{display:flex;flex-direction:column;gap:8px;overflow:auto;padding-right:6px} -.queue-item{display:flex;align-items:center;gap:12px;padding:8px;border-radius:8px;border:1px solid #eef2f7;background:linear-gradient(180deg,#fff,#fbfdff)} -.thumb{width:56px;height:56px;border-radius:6px;background:linear-gradient(180deg,#eef2ff,#fff);display:flex;align-items:center;justify-content:center;color:#0f172a;font-weight:700} -.item-main{flex:1;min-width:0} -.item-title{font-weight:600;text-overflow:ellipsis;overflow:hidden;white-space:nowrap} +.queue-list{display:flex;flex-direction:column;gap:8px;overflow:auto;padding-right:6px;max-height:calc(100vh - 200px)} +.queue-item{display:flex;align-items:center;gap:12px;padding:8px;border-radius:8px;border:1px solid #eef2f7;background:linear-gradient(180deg,#fff,#fbfdff);min-height:48px} +.thumb{width:56px;height:56px;border-radius:6px;background:linear-gradient(180deg,#eef2ff,#fff);display:flex;align-items:center;justify-content:center;color:#0f172a;font-weight:700;flex-shrink:0} +.item-main{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px} +.item-title{font-weight:600;text-overflow:ellipsis;overflow:hidden;white-space:nowrap;font-size:14px} .item-title.clickable{ cursor:pointer; transition:all 0.2s ease; @@ -133,8 +259,18 @@ button.chip[aria-pressed="false"]{opacity:0.55;filter:grayscale(0.15)} transform:translateY(-1px); box-shadow:0 2px 8px rgba(14,165,164,0.15); } -.item-meta{display:flex;gap:8px;align-items:center;margin-top:6px;font-size:12px;color:var(--muted)} -.item-actions{display:flex;align-items:center} +.item-meta{display:flex;gap:8px;align-items:center;margin-top:2px;font-size:12px;color:var(--muted);flex-wrap:wrap} +.item-actions{display:flex;align-items:center;gap:8px;flex-shrink:0} + +/* Ensure proper spacing in queue items */ +.queue-item > * { + flex-shrink: 0; +} + +.queue-item .item-main { + flex-shrink: 1; + min-width: 0; +} /* drawer */ .drawer{ @@ -168,6 +304,73 @@ button.chip[aria-pressed="false"]{opacity:0.55;filter:grayscale(0.15)} /* marker custom */ .leaflet-container .custom-marker{display:flex;align-items:center;justify-content:center} +/* date range styling */ +.filter-group .row { + display:flex; + align-items:center; + justify-content:space-between; + margin-bottom:12px; +} + +.filter-group .row strong { + font-size:14px; + font-weight:600; + color:#111827; +} + +.filter-group .date-inputs { + display:flex; + gap:8px; + margin-top:8px; + align-items:flex-end; +} + +.filter-group .date-input-group { + flex:1; + min-width:0; + display:flex; + flex-direction:column; +} + +.filter-group .date-input-group label { + font-size:12px; + font-weight:500; + color:#6b7280; + margin-bottom:4px; + white-space:nowrap; +} + +.filter-group .date-input-group input[type="date"] { + padding:6px 8px; + border:1px solid #d1d5db; + border-radius:6px; + font-size:14px; + background:white; + transition:border-color 0.2s ease, box-shadow 0.2s ease; + width:100%; + box-sizing:border-box; +} + +.filter-group .date-input-group input[type="date"]:focus { + outline:none; + border-color:var(--accent); + box-shadow:0 0 0 3px rgba(14,165,164,0.1); +} + +/* button group styling */ +.filter-group .button-group { + display:flex; + gap:8px; + margin-top:16px; + justify-content:flex-end; +} + +.filter-group .button-group .btn { + padding:8px 16px; + font-size:13px; + min-width:60px; +} + /* small screens */ @media (max-width:900px){ .main{grid-template-columns:1fr;grid-auto-rows:auto} @@ -176,7 +379,61 @@ button.chip[aria-pressed="false"]{opacity:0.55;filter:grayscale(0.15)} .header{padding:8px 12px} .filters{order:2} .map-panel{order:1} - .panel{padding:10px} + .panel{padding:16px} + .filter-group .date-inputs { + flex-direction:column; + gap:12px; + } + .filter-group .date-input-group { + min-width:auto; + } + .filter-group .button-group { + flex-direction:column; + gap:8px; + } + .queue-item .item-actions { + flex-direction:column; + align-items:stretch; + gap:6px; + min-width:auto; + } + .queue-item .item-actions select, + .queue-item .item-actions button { + width:100%; + text-align:center; + } +} + +/* medium screens - adjust for better button layout */ +@media (max-width:1200px) and (min-width:901px){ + .main{grid-template-columns:280px 1fr 360px} +} + +/* footer styling */ +.footer{ + background:var(--panel); + border-radius:12px; + padding:16px 20px; + display:flex; + justify-content:space-between; + align-items:center; + border:1px solid rgba(0,0,0,0.05); + box-shadow:0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); +} + +.stats{ + display:flex; + align-items:center; + gap:16px; + font-size:14px; +} + +.stats > div{ + display:flex; + align-items:center; + gap:6px; + color:#374151; + font-weight:500; } /* accessibility tweaks */