fiddy/apps/web/features/groups/components/group-settings-modals.tsx

214 lines
11 KiB
TypeScript

"use client";
import type { GroupSettingsViewModelState } from "@/features/groups/components/use-group-settings-view-model";
import TagInput from "@/shared/components/forms/tag-input";
import ConfirmRetypeModal from "@/shared/components/modals/confirm-retype-modal";
import ConfirmSlideModal from "@/shared/components/modals/confirm-slide-modal";
type GroupSettingsModalsProps = {
vm: GroupSettingsViewModelState;
};
export default function GroupSettingsModals({ vm }: GroupSettingsModalsProps) {
return (
<>
{vm.renameModalOpen ? (
<div className="fixed inset-0 z-[70] flex items-center justify-center bg-black/60 p-4 !mt-0" onClick={vm.handleCloseRenameModal}>
<div
className="relative w-full max-w-sm rounded-2xl border border-accent-weak bg-panel p-5"
onClick={event => event.stopPropagation()}
onKeyDown={event => {
if (event.key === "Escape") vm.handleCloseRenameModal();
if (event.key === "Enter" && vm.renameDirty && vm.isAdmin && vm.renameValue.trim()) vm.setConfirmRenameOpen(true);
}}
role="dialog"
tabIndex={-1}
>
<button
type="button"
className="absolute right-3 top-3 rounded-lg btn-outline-accent px-2 py-1 text-sm"
onClick={vm.handleCloseRenameModal}
aria-label="Close"
>
x
</button>
<div className="text-lg font-semibold">Change group name</div>
<input
className={`mt-4 w-full input-base px-3 py-2 text-sm ${vm.renameValue.trim() ? "" : "border-red-400/70"}`}
value={vm.renameValue}
onChange={event => vm.setRenameValue(event.target.value)}
placeholder="Group name"
/>
{vm.renameDirty ? (
<div className="mt-3 rounded-lg border border-yellow-400/60 bg-yellow-500/10 px-3 py-2 text-xs text-yellow-200">
You have unsaved changes.
</div>
) : null}
<div className="mt-4 flex items-center gap-2">
{vm.renameDirty ? (
<>
<button
type="button"
className="flex-1 rounded-lg btn-outline-accent px-3 py-2 text-sm font-semibold"
onClick={vm.handleCloseRenameModal}
>
Discard changes
</button>
<button
type="button"
className="flex-1 rounded-lg btn-accent px-3 py-2 text-sm font-semibold disabled:opacity-60"
onClick={() => vm.setConfirmRenameOpen(true)}
disabled={!vm.isAdmin || !vm.renameValue.trim()}
>
Rename
</button>
</>
) : (
<button
type="button"
className="flex-1 rounded-lg btn-outline-accent px-3 py-2 text-sm font-semibold"
onClick={vm.handleCloseRenameModal}
>
Dismiss
</button>
)}
</div>
</div>
</div>
) : null}
{vm.tagModalOpen ? (
<div className="fixed inset-0 z-[70] flex items-center justify-center bg-black/60 p-4 !mt-0" onClick={() => vm.setTagModalOpen(false)}>
<div
className="w-full max-w-xl rounded-2xl border border-accent-weak bg-panel p-5"
onClick={event => event.stopPropagation()}
onKeyDown={event => {
if (event.key === "Escape") vm.setTagModalOpen(false);
}}
role="dialog"
tabIndex={-1}
>
<div className="flex items-center justify-between">
<div className="text-lg font-semibold">Edit tags</div>
<button type="button" className="rounded-lg btn-outline-accent px-2 py-1 text-sm" onClick={() => vm.setTagModalOpen(false)} aria-label="Close">x</button>
</div>
<div className="mt-4 space-y-3">
<TagInput
label="Add tags"
tags={vm.pendingTags}
suggestions={vm.tags}
enableBackspaceRemove
onToggleTag={tag => vm.setPendingTags(prev => prev.filter(item => item !== tag))}
onAddTag={tag => vm.setPendingTags(prev => (prev.includes(tag) ? prev : [...prev, tag]))}
/>
<button
type="button"
className="rounded-lg btn-accent px-3 py-2 text-sm font-semibold disabled:opacity-60"
onClick={vm.handleSaveTags}
disabled={!vm.pendingTags.length || !vm.canManageTags}
>
Save tags
</button>
{!vm.canManageTags ? (
<div className="text-xs text-soft">Only admins can add new tags.</div>
) : null}
<div className="divider" />
<div className="text-sm font-semibold">Existing tags</div>
<div className="max-h-60 overflow-auto resize-y pr-1">
<div className="flex flex-wrap gap-2">
{vm.tags.map(tag => (
<button
key={tag}
type="button"
className={`rounded-full border px-2 py-0.5 text-xs text-[color:var(--color-text)] ${vm.toggleRemoveTags.includes(tag) ? "border-red-400/60 text-red-200 bg-red-500/10" : "border-accent-weak bg-accent-soft hover:border-accent"}`}
onClick={() => {
if (!vm.canManageTags) return;
vm.setToggleRemoveTags(prev => (prev.includes(tag) ? prev.filter(item => item !== tag) : [...prev, tag]));
}}
>
#{tag}
</button>
))}
{!vm.tags.length ? <div className="text-xs text-soft">No tags yet.</div> : null}
</div>
</div>
{vm.toggleRemoveTags.length ? (
<button
type="button"
className="rounded-lg border border-red-400/60 bg-red-500/10 px-3 py-2 text-sm font-semibold text-red-200"
onClick={() => vm.setConfirmDeleteOpen(true)}
>
Delete selected ({vm.toggleRemoveTags.length})
</button>
) : null}
</div>
</div>
</div>
) : null}
<ConfirmSlideModal
isOpen={vm.confirmDeleteOpen}
title="Delete selected tags"
description="Tags will be removed from this group and any entries using them."
confirmLabel="Delete tags"
onClose={() => vm.setConfirmDeleteOpen(false)}
onConfirm={() => {
vm.setConfirmDeleteOpen(false);
vm.handleConfirmDelete();
}}
/>
<ConfirmSlideModal
isOpen={Boolean(vm.confirmDeleteInvite)}
title="Delete invite link"
description={vm.confirmDeleteInvite ? `Delete invite ${vm.confirmDeleteInvite.token.slice(0, 6)}...${vm.confirmDeleteInvite.token.slice(-4)}?` : ""}
confirmLabel="Delete link"
onClose={() => vm.setConfirmDeleteInvite(null)}
onConfirm={vm.handleConfirmDeleteInvite}
/>
<ConfirmSlideModal
isOpen={vm.confirmRenameOpen}
title="Rename group"
description={`Change group name to \"${vm.renameValue.trim()}\"?`}
confirmLabel="Rename"
onClose={() => vm.setConfirmRenameOpen(false)}
onConfirm={vm.handleRenameGroup}
/>
<ConfirmSlideModal
isOpen={vm.confirmLeaveOpen}
title="Leave group"
description="You will lose access to this group."
confirmLabel="Leave"
onClose={() => vm.setConfirmLeaveOpen(false)}
onConfirm={vm.handleConfirmLeaveGroup}
/>
<ConfirmSlideModal
isOpen={Boolean(vm.confirmKick)}
title="Kick member"
description={vm.confirmKick ? `Remove ${vm.confirmKick.name} from this group?` : ""}
confirmLabel="Kick"
onClose={() => vm.setConfirmKick(null)}
onConfirm={vm.handleConfirmKickMember}
/>
<ConfirmSlideModal
isOpen={Boolean(vm.confirmTransfer)}
title="Transfer ownership"
description={vm.confirmTransfer ? `Make ${vm.confirmTransfer.name} the new owner?` : ""}
confirmLabel="Transfer"
onClose={() => vm.setConfirmTransfer(null)}
onConfirm={vm.handleConfirmTransferOwnership}
/>
<ConfirmRetypeModal
isOpen={vm.confirmDeleteGroupOpen}
title="Delete group"
description="Type DELETE to confirm. This cannot be undone."
expectedText="DELETE"
value={vm.deleteConfirmText}
onChange={vm.setDeleteConfirmText}
confirmLabel="Delete"
onClose={() => vm.setConfirmDeleteGroupOpen(false)}
onConfirm={vm.handleDeleteGroup}
/>
</>
);
}