feat : dont conflit with other editor
This commit is contained in:
@@ -7,7 +7,7 @@ import { Button } from "@/components/ui/button";
|
||||
import { Spinner } from "@/components/ui/spinner";
|
||||
import type { AutosaveState } from "@/hooks/use-autosave";
|
||||
|
||||
export function AutosaveStatus({ state, issues = [], onSave }: { state: AutosaveState; issues?: readonly string[]; onSave?: () => void }) {
|
||||
export function AutosaveStatus({ state, issues = [], onSave, conflictVersion, canOverwrite = false, onReloadLatest, onOverwrite }: { state: AutosaveState; issues?: readonly string[]; onSave?: () => void; conflictVersion?: number | null; canOverwrite?: boolean; onReloadLatest?: () => void; onOverwrite?: () => void }) {
|
||||
const [showIssues, setShowIssues] = useState(false);
|
||||
|
||||
if (state === "saving") return <Badge variant="secondary"><Spinner data-icon="inline-start" />Saving…</Badge>;
|
||||
@@ -27,6 +27,19 @@ export function AutosaveStatus({ state, issues = [], onSave }: { state: Autosave
|
||||
</AlertDescription>
|
||||
</Alert> : null}
|
||||
</div>;
|
||||
if (state === "conflict") return <Alert variant="destructive" className="max-w-xl">
|
||||
<AlertCircleIcon aria-hidden="true" />
|
||||
<AlertTitle>มีการบันทึกข้อมูลใหม่จากผู้แก้ไขคนอื่น</AlertTitle>
|
||||
<AlertDescription>
|
||||
<div className="flex flex-col gap-3">
|
||||
<p>ระบบหยุด Autosave ไว้เพื่อไม่ให้ Draft นี้เขียนทับข้อมูลล่าสุด{conflictVersion ? ` (version ${conflictVersion})` : ""}</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{onReloadLatest ? <Button type="button" variant="outline" size="sm" onClick={onReloadLatest}>โหลดข้อมูลล่าสุด</Button> : null}
|
||||
{onOverwrite ? <Button type="button" variant="destructive" size="sm" disabled={!canOverwrite} onClick={onOverwrite}>เขียนทับด้วย Draft นี้</Button> : null}
|
||||
</div>
|
||||
</div>
|
||||
</AlertDescription>
|
||||
</Alert>;
|
||||
if (state === "error") return <div className="flex items-center gap-2"><Badge variant="destructive"><AlertCircleIcon data-icon="inline-start" />Autosave failed</Badge>{onSave ? <Button type="button" variant="outline" size="sm" onClick={onSave}>Save now</Button> : null}</div>;
|
||||
return <Badge variant="secondary"><CheckIcon data-icon="inline-start" />Saved</Badge>;
|
||||
}
|
||||
|
||||
@@ -203,6 +203,8 @@ export function GuideOverviewForm({
|
||||
const invalidOverview = !overview.trim();
|
||||
const localDraft = { overview, youtubeLinks, cover, posterArt, posterArtOffset, posterArtScale, posterTopColors, posterBottomColors, bleeding };
|
||||
const autosave = useAutosave({
|
||||
guideId: guide.id,
|
||||
scope: "overview",
|
||||
value,
|
||||
valid: Boolean(cover.id && overview.trim() && !invalidYoutubeLinks),
|
||||
initialVersion: guide.version,
|
||||
@@ -512,6 +514,10 @@ export function GuideOverviewForm({
|
||||
<CardFooter className="justify-end">
|
||||
<AutosaveStatus
|
||||
state={autosave.state}
|
||||
conflictVersion={autosave.conflictVersion}
|
||||
canOverwrite={autosave.canOverwrite}
|
||||
onReloadLatest={autosave.reloadLatest}
|
||||
onOverwrite={autosave.overwrite}
|
||||
issues={incompleteIssues}
|
||||
onSave={autosave.saveNow}
|
||||
/>
|
||||
|
||||
@@ -1375,6 +1375,8 @@ function WeaponEditor({ guide, weapons, characters, initial }: EditorBase) {
|
||||
!validation.success && hasIssuePrefix(validation.error.issues, ...path);
|
||||
const localDraft = { note, groups, activeGroupIndex };
|
||||
const autosave = useAutosave({
|
||||
guideId: guide.id,
|
||||
scope: "weapon",
|
||||
value,
|
||||
valid: validation.success,
|
||||
initialVersion: guide.version,
|
||||
@@ -1728,6 +1730,10 @@ function WeaponEditor({ guide, weapons, characters, initial }: EditorBase) {
|
||||
</Button>
|
||||
<AutosaveStatus
|
||||
state={autosave.state}
|
||||
conflictVersion={autosave.conflictVersion}
|
||||
canOverwrite={autosave.canOverwrite}
|
||||
onReloadLatest={autosave.reloadLatest}
|
||||
onOverwrite={autosave.overwrite}
|
||||
issues={
|
||||
validation.success
|
||||
? []
|
||||
@@ -1874,6 +1880,8 @@ function ArtifactEditor({
|
||||
note,
|
||||
};
|
||||
const autosave = useAutosave({
|
||||
guideId: guide.id,
|
||||
scope: "artifact",
|
||||
value,
|
||||
valid: validation.success,
|
||||
initialVersion: guide.version,
|
||||
@@ -2494,6 +2502,10 @@ function ArtifactEditor({
|
||||
<CardFooter className="justify-end">
|
||||
<AutosaveStatus
|
||||
state={autosave.state}
|
||||
conflictVersion={autosave.conflictVersion}
|
||||
canOverwrite={autosave.canOverwrite}
|
||||
onReloadLatest={autosave.reloadLatest}
|
||||
onOverwrite={autosave.overwrite}
|
||||
issues={
|
||||
validation.success
|
||||
? []
|
||||
@@ -2571,6 +2583,8 @@ function ConstellationEditor({
|
||||
!validation.success && hasIssuePrefix(validation.error.issues, ...path);
|
||||
const localDraft = { note, groups };
|
||||
const autosave = useAutosave({
|
||||
guideId: guide.id,
|
||||
scope: "constellations",
|
||||
value,
|
||||
valid: validation.success,
|
||||
initialVersion: guide.version,
|
||||
@@ -2789,6 +2803,10 @@ function ConstellationEditor({
|
||||
<CardFooter className="justify-end">
|
||||
<AutosaveStatus
|
||||
state={autosave.state}
|
||||
conflictVersion={autosave.conflictVersion}
|
||||
canOverwrite={autosave.canOverwrite}
|
||||
onReloadLatest={autosave.reloadLatest}
|
||||
onOverwrite={autosave.overwrite}
|
||||
issues={
|
||||
validation.success
|
||||
? []
|
||||
@@ -3011,6 +3029,8 @@ function TeamEditor({
|
||||
}
|
||||
}, [guide.id, presets, presetsGuideId, presetsHydrated]);
|
||||
const autosave = useAutosave({
|
||||
guideId: guide.id,
|
||||
scope: "team",
|
||||
value,
|
||||
valid: validation.success,
|
||||
initialVersion: guide.version,
|
||||
@@ -3918,6 +3938,10 @@ function TeamEditor({
|
||||
</Button>
|
||||
<AutosaveStatus
|
||||
state={autosave.state}
|
||||
conflictVersion={autosave.conflictVersion}
|
||||
canOverwrite={autosave.canOverwrite}
|
||||
onReloadLatest={autosave.reloadLatest}
|
||||
onOverwrite={autosave.overwrite}
|
||||
issues={
|
||||
validation.success
|
||||
? []
|
||||
@@ -4448,6 +4472,8 @@ function ExtraEditor({ guide, extraSectionId, initial, characters, weapons, arti
|
||||
!validation.success && hasIssuePrefix(validation.error.issues, ...path);
|
||||
const localDraft = { sections };
|
||||
const autosave = useAutosave({
|
||||
guideId: guide.id,
|
||||
scope: `extra:${sectionId}`,
|
||||
value,
|
||||
valid: validation.success,
|
||||
initialVersion: guide.version,
|
||||
@@ -5058,6 +5084,10 @@ function ExtraEditor({ guide, extraSectionId, initial, characters, weapons, arti
|
||||
<CardFooter className="flex-wrap justify-end gap-3">
|
||||
<AutosaveStatus
|
||||
state={autosave.state}
|
||||
conflictVersion={autosave.conflictVersion}
|
||||
canOverwrite={autosave.canOverwrite}
|
||||
onReloadLatest={autosave.reloadLatest}
|
||||
onOverwrite={autosave.overwrite}
|
||||
issues={
|
||||
validation.success
|
||||
? []
|
||||
|
||||
Reference in New Issue
Block a user