Merge branch 'feat/env-doctor' into feat/template-catalog

This commit is contained in:
Tomas Dvorak committed 2026-10-09 00:37:10 +02:00
commit 9f8eaed386
1 file changed
+12 -25
@@ -480,6 +480,7 @@ export function ServiceDetailPage() {
mutationFn: (input: Parameters<typeof updateService>[1]) => updateService(serviceId, input), mutationFn: (input: Parameters<typeof updateService>[1]) => updateService(serviceId, input),
onSuccess: () => { onSuccess: () => {
setNetworkForm(null); setNetworkForm(null);
setSleepDraft(null);
invalidateService(); invalidateService();
}, },
}); });
@@ -498,24 +499,7 @@ export function ServiceDetailPage() {
const [domainInput, setDomainInput] = useState(''); const [domainInput, setDomainInput] = useState('');
const [domainChecks, setDomainChecks] = useState<Record<string, DomainCheckResult> | null>(null); const [domainChecks, setDomainChecks] = useState<Record<string, DomainCheckResult> | null>(null);
const [accessForm, setAccessForm] = useState<{ maintenance: boolean; basicAuth: string } | null>(null); const [accessForm, setAccessForm] = useState<{ maintenance: boolean; basicAuth: string } | null>(null);
const [sleepForm, setSleepForm] = useState<{ enabled: boolean; minutes: string; dirty: boolean }>({ const [sleepDraft, setSleepDraft] = useState<{ id: string; enabled: boolean; minutes: string } | null>(null);
enabled: false,
minutes: '15',
dirty: false,
});
const sleepFormInitRef = useRef<string>('');
useEffect(() => {
const svc = isDemoMode ? getDemoServiceById(serviceId) : serviceQuery.data;
const key = svc ? `${svc.id}:${svc.sleepEnabled}:${svc.sleepIdleMinutes}` : '';
if (svc && key !== sleepFormInitRef.current) {
sleepFormInitRef.current = key;
setSleepForm({
enabled: Boolean(svc.sleepEnabled),
minutes: String(svc.sleepIdleMinutes ?? 15),
dirty: false,
});
}
}, [isDemoMode, serviceId, serviceQuery.data]);
const [buildForm, setBuildForm] = useState<{ const [buildForm, setBuildForm] = useState<{
builder: string; cpuReserve: string; memoryReserve: string; staticCmd: string; staticDir: string; builder: string; cpuReserve: string; memoryReserve: string; staticCmd: string; staticDir: string;
} | null>(null); } | null>(null);
@@ -536,6 +520,9 @@ export function ServiceDetailPage() {
const project = isDemoMode ? getDemoProjectById(projectId) : projectQuery.data; const project = isDemoMode ? getDemoProjectById(projectId) : projectQuery.data;
const service = isDemoMode ? getDemoServiceById(serviceId) : serviceQuery.data; const service = isDemoMode ? getDemoServiceById(serviceId) : serviceQuery.data;
const sleepDraftCurrent = sleepDraft?.id === serviceId ? sleepDraft : null;
const sleepEnabled = sleepDraftCurrent?.enabled ?? Boolean(service?.sleepEnabled);
const sleepMinutes = sleepDraftCurrent?.minutes ?? String(service?.sleepIdleMinutes ?? 15);
const varRows: VariableDraft[] = useMemo( const varRows: VariableDraft[] = useMemo(
() => () =>
@@ -2424,9 +2411,9 @@ export function ServiceDetailPage() {
<label className="flex items-center gap-2 text-[var(--text-primary)]"> <label className="flex items-center gap-2 text-[var(--text-primary)]">
<input <input
type="checkbox" type="checkbox"
checked={Boolean(sleepForm.enabled)} checked={sleepEnabled}
onChange={(e) => onChange={(e) =>
setSleepForm((f) => ({ ...f, enabled: e.target.checked, dirty: true })) setSleepDraft({ id: serviceId, enabled: e.target.checked, minutes: sleepMinutes })
} }
/> />
Sleep on idle Sleep on idle
@@ -2437,9 +2424,9 @@ export function ServiceDetailPage() {
type="number" type="number"
min={1} min={1}
max={1440} max={1440}
value={sleepForm.minutes} value={sleepMinutes}
onChange={(e) => onChange={(e) =>
setSleepForm((f) => ({ ...f, minutes: e.target.value, dirty: true })) setSleepDraft({ id: serviceId, enabled: sleepEnabled, minutes: e.target.value })
} }
className="w-20 px-2 py-1 rounded-[var(--radius-md)] bg-[var(--surface-muted)] border border-[var(--border-subtle)] text-sm text-[var(--text-primary)]" className="w-20 px-2 py-1 rounded-[var(--radius-md)] bg-[var(--surface-muted)] border border-[var(--border-subtle)] text-sm text-[var(--text-primary)]"
/> />
@@ -2450,15 +2437,15 @@ export function ServiceDetailPage() {
disabled={updateServiceMutation.isPending} disabled={updateServiceMutation.isPending}
onClick={() => onClick={() =>
updateServiceMutation.mutate({ updateServiceMutation.mutate({
sleep_enabled: sleepForm.enabled, sleep_enabled: sleepEnabled,
sleep_idle_minutes: Math.max(1, Math.min(1440, Number(sleepForm.minutes) || 15)), sleep_idle_minutes: Math.max(1, Math.min(1440, Number(sleepMinutes) || 15)),
}) })
} }
className="px-3 py-1.5 rounded-[var(--radius-md)] bg-[var(--accent-primary)] text-[var(--accent-on)] text-xs font-medium disabled:opacity-50" className="px-3 py-1.5 rounded-[var(--radius-md)] bg-[var(--accent-primary)] text-[var(--accent-on)] text-xs font-medium disabled:opacity-50"
> >
Save Save
</button> </button>
{sleepForm.dirty && ( {sleepDraftCurrent && (
<span className="text-[var(--text-tertiary)]">unsaved</span> <span className="text-[var(--text-tertiary)]">unsaved</span>
)} )}
</div> </div>