sync: update from private repo (e62f5c7)
CI / build-and-test (push) Has been cancelled

This commit is contained in:
oss-sync
2026-06-11 01:52:48 +00:00
parent 000a2474aa
commit d061ad08d8
237 changed files with 8441 additions and 5549 deletions
+14 -8
View File
@@ -1,4 +1,5 @@
import { useState, useEffect, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { useQueryClient } from '@tanstack/react-query';
import { useConfig } from '../../hooks/useConfig';
import { useUnsavedGuard } from '../../lib/unsavedGuard';
@@ -81,6 +82,7 @@ function countDiff(a: any, b: any): number {
}
export function ConfigForm({ section, isAdmin }: ConfigFormProps) {
const { t } = useTranslation('settings');
// User-scoped sections use their own per-user APIs and should not load the
// admin /api/config draft. Render them stand-alone without the global save bar.
if (section === 'preferences') {
@@ -93,7 +95,7 @@ export function ConfigForm({ section, isAdmin }: ConfigFormProps) {
return <div className="max-w-2xl"><MemoryLearningForm /></div>;
}
if (!isAdmin) {
return <div className="max-w-2xl text-sm text-slate-500"></div>;
return <div className="max-w-2xl text-sm text-slate-500">{t('configForm.adminOnly')}</div>;
}
// Local organizations: admin-managed via /api/admin/orgs (not config.yaml),
// so render stand-alone without the global save bar.
@@ -108,6 +110,7 @@ export function ConfigForm({ section, isAdmin }: ConfigFormProps) {
}
function ConfigFormInner({ section }: ConfigFormProps) {
const { t } = useTranslation('settings');
const { data, isLoading, error, refetch } = useConfig();
const queryClient = useQueryClient();
@@ -117,6 +120,7 @@ function ConfigFormInner({ section }: ConfigFormProps) {
const [isDirty, setIsDirty] = useState(false);
const [saving, setSaving] = useState(false);
const [toast, setToast] = useState<string | null>(null);
const [toastIsError, setToastIsError] = useState(false);
// Sync fetched config into draft
useEffect(() => {
@@ -146,17 +150,19 @@ function ConfigFormInner({ section }: ConfigFormProps) {
try {
const result = await updateConfig(draft, etag);
if (result.conflict) {
if (confirm('設定が他で変更されました。再読み込みしますか?')) {
if (confirm(t('configForm.conflict'))) {
await refetch();
}
return;
}
await queryClient.invalidateQueries({ queryKey: ['config'] });
setIsDirty(false);
setToast('保存しました');
setToastIsError(false);
setToast(t('configForm.saved'));
setTimeout(() => setToast(null), 2000);
} catch (e: any) {
setToast(`エラー: ${e.message}`);
setToastIsError(true);
setToast(t('configForm.error', { msg: e.message }));
setTimeout(() => setToast(null), 3000);
} finally {
setSaving(false);
@@ -169,7 +175,7 @@ function ConfigFormInner({ section }: ConfigFormProps) {
useUnsavedGuard(dirtyCount > 0);
if (isLoading) return <div className="text-sm text-slate-400">Loading...</div>;
if (error) return <div className="text-sm text-red-500"></div>;
if (error) return <div className="text-sm text-red-500">{t('configForm.loadError')}</div>;
if (!draft) return null;
const formProps = { config: draft, onChange: handleChange, overriddenByEnv };
@@ -259,15 +265,15 @@ function ConfigFormInner({ section }: ConfigFormProps) {
}`}
>
{toast ? (
<span className={`text-2xs mr-auto ${toast.startsWith('エラー') ? 'text-red-600' : 'text-emerald-700 dark:text-emerald-300'}`}>
<span className={`text-2xs mr-auto ${toastIsError ? 'text-red-600' : 'text-emerald-700 dark:text-emerald-300'}`}>
{toast}
</span>
) : dirty ? (
<span className="text-xs mr-auto text-amber-800 dark:text-amber-300 flex items-center gap-1.5 font-medium min-w-0">
<span className="inline-block w-1.5 h-1.5 rounded-full bg-amber-500 animate-pulse flex-shrink-0" aria-hidden />
<span className="truncate">
<span className="hidden sm:inline">: {dirtyCount} Save &amp; Apply</span>
<span className="sm:hidden"> {dirtyCount}</span>
<span className="hidden sm:inline">{t('configForm.unsaved', { count: dirtyCount })}</span>
<span className="sm:hidden">{t('configForm.unsavedShort', { count: dirtyCount })}</span>
</span>
</span>
) : null}