Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,9 +278,20 @@ def prometheus_metrics() -> Response:
return metrics_response()


class _NoCacheStatic(StaticFiles):
"""Dashboard SPA: no-cache header — deploy/değişiklik sonrası tarayıcı ESKİ asset
sunmaz (ETag korunur → değişmeyen dosyada 304 hızlı, ama her zaman revalidate).
Önceki davranış: ETag var ama Cache-Control yok → tarayıcı heuristik cache'liyor."""

async def get_response(self, path, scope):
resp = await super().get_response(path, scope)
resp.headers["Cache-Control"] = "no-cache, must-revalidate"
return resp


_dashboard_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), "frontend")
if os.path.isdir(_dashboard_dir):
app.mount("/dashboard", StaticFiles(directory=_dashboard_dir, html=True), name="dashboard")
app.mount("/dashboard", _NoCacheStatic(directory=_dashboard_dir, html=True), name="dashboard")

# Bitki sağlığı görsel upload'ları (plants.py içinde URL üretiliyor)
_plant_uploads_dir = os.path.join(os.path.dirname(__file__), "ml", "plant_uploads")
Expand Down
4 changes: 2 additions & 2 deletions frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -873,7 +873,7 @@ <h2 id="pageTitle">Dashboard</h2>
<stop offset="100%" stop-color="#f59e0b"/>
</radialGradient>
</defs>
<circle cx="12" cy="12" r="10" fill="url(#sunGrad)"/>
<circle cx="12" cy="12" r="12" fill="url(#sunGrad)"/>
<ellipse cx="9" cy="10" rx="2.5" ry="1.8" fill="#fef9c3" opacity=".6"/>
</svg>
<svg class="knob-moon" viewBox="0 0 24 24">
Expand All @@ -884,7 +884,7 @@ <h2 id="pageTitle">Dashboard</h2>
<stop offset="100%" stop-color="#94a3b8"/>
</radialGradient>
</defs>
<circle cx="12" cy="12" r="10" fill="url(#moonGrad)"/>
<circle cx="12" cy="12" r="12" fill="url(#moonGrad)"/>
<circle cx="14.5" cy="9" r="1.6" fill="#94a3b8" opacity=".55"/>
<circle cx="9" cy="13.5" r="1.2" fill="#94a3b8" opacity=".5"/>
<circle cx="15" cy="15" r="1" fill="#94a3b8" opacity=".5"/>
Expand Down
6 changes: 4 additions & 2 deletions frontend/src/lib/pages/account.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import { _fmtDate, _escAttr, showToast } from "../utils.js";
import { getAuthToken, setAuthToken, clearAuthToken, apiAuth, API_BASE } from "../api.js";
import { _skeletonBlock, _setBusy } from "../skeleton.js";
import { setCurrentUser } from "../session.js";
import { setCurrentUser, getCurrentUser } from "../session.js";
import { navigate } from "../nav.js";
import { refreshBell, _hideBell } from "./alerts.js";

Expand Down Expand Up @@ -317,11 +317,13 @@ export async function loadUsers() {
.map(r => `<option value="${r}"${r === sel ? ' selected' : ''}>${ROLE_LABELS[r]}</option>`).join('');
let html = '<table class="detail-table"><caption class="sr-only">Kullanıcı listesi</caption><thead><tr>'
+ '<th>Ad</th><th>E-posta</th><th>Rol</th><th>Çiftlik</th><th>Kayıt</th><th>İşlem</th></tr></thead><tbody>';
const me = getCurrentUser();
for (const u of list) {
const isSelf = me && u.id === me.id;
html += `<tr>
<td>${_escAttr(u.name)}</td>
<td>${_escAttr(u.email)}</td>
<td><select class="user-role-select" data-action="changeUserRole" data-id="${u.id}">${roleOpts(u.role)}</select></td>
<td><select class="user-role-select" data-action="changeUserRole" data-id="${u.id}"${isSelf ? ' disabled title="Kendi rolünü değiştiremezsin — lock-out koruması (başka bir admin değiştirmeli)"' : ''}>${roleOpts(u.role)}</select></td>
<td>${u.owned_farms_count ?? 0}</td>
<td>${_fmtDate(u.created_at)}</td>
<td class="user-actions">
Expand Down
15 changes: 14 additions & 1 deletion frontend/src/lib/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
Hash-based SPA navigation, sidebar toggle, page title yönetimi.
============================================================ */

import { getCurrentUser } from "./session.js";

export const pageTitles = {
dashboard: ['Genel Bakış', 'Tarlanın özeti'],
fields: ['Tarlalarım', 'Çiftliklerine bağlı tarlalar'],
Expand All @@ -20,6 +22,15 @@ export const pageTitles = {
auth: ['Hesabım', 'Profil ve şifre'],
};

// Sistem rolleri (developer/overseer/admin) TÜM sistemi görür → çiftçi-odaklı
// ("kendi tarlan") alt başlıklar yanıltıcı. Bu sayfalarda kapsam-doğru metin:
const SYSTEM_SUBTITLES = {
dashboard: 'Sistem geneli özet',
fields: 'Tüm çiftlikler ve tarlalar',
sensors: 'Sistemdeki tüm sensörler',
irrigation: 'Tüm sulama kayıtları ve önerisi',
};

/**
* Sayfa değiştirme fonksiyonu.
* @param {string} page - Sayfa adı
Expand All @@ -45,7 +56,9 @@ export function navigate(page, handlers, startHeroTipRotation) {
navItem.setAttribute('aria-current', 'page');
}
document.getElementById('pageTitle').textContent = pageTitles[page][0];
document.getElementById('pageSubtitle').textContent = pageTitles[page][1];
const _u = getCurrentUser();
const _sysSub = _u && _u.role !== 'farmer' && SYSTEM_SUBTITLES[page];
document.getElementById('pageSubtitle').textContent = _sysSub || pageTitles[page][1];
// Sayfa her zaman TEPEDEN açılsın + a11y odağı (odak kaydırmasın → preventScroll).
window.scrollTo(0, 0);
const main = document.getElementById('main-content');
Expand Down
9 changes: 6 additions & 3 deletions frontend/src/styles/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,21 @@ input, select { font-family: var(--font); }
/* Selection highlight */
::selection { background: rgba(16,185,129,.28); color: var(--text); }

/* Global scrollbar */
/* Global scrollbar — SADECE ::-webkit (Chrome/Safari kalıcı özel scrollbar).
scrollbar-color KULLANMA: modern Chrome onu görünce ::-webkit'i yok sayıp ince
standart (overlay) scrollbar'a geçiyor → scrollbar "kayboluyor".
Gece varsayılan = slate; gündüz (yeşil) override → theme-light.css. */
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
background: rgba(16,185,129,.28);
background: rgba(100,116,139,.55);
border-radius: 6px;
border: 2px solid transparent;
background-clip: padding-box;
transition: background .2s ease;
}
*::-webkit-scrollbar-thumb:hover {
background: rgba(16,185,129,.55);
background: rgba(148,163,184,.72);
background-clip: padding-box;
}

Expand Down
4 changes: 4 additions & 0 deletions frontend/src/styles/theme-light.css
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,7 @@
[data-theme="light"] .theme-toggle:hover .toggle-track {
box-shadow: inset 0 2px 6px rgba(15,23,42,.2), 0 0 0 1px rgba(96,165,250,.4);
}

/* Light tema scrollbar — ORİJİNAL marka yeşili (açık zeminde mükemmeldi, korundu) */
[data-theme="light"] *::-webkit-scrollbar-thumb { background: rgba(16,185,129,.28); }
[data-theme="light"] *::-webkit-scrollbar-thumb:hover { background: rgba(16,185,129,.55); }
Loading