feat(ux): convert all hand-rolled modals to accessible Radix Dialog
S3 (final Tier 1 item). ops (4 modals), members/tasks/projects/ payments/contacts add-edit modals, settings integration-credentials modal, and the payments link-member modal now use components/ui/dialog (focus trap, ESC, aria, scroll lock for free). Forms, fields, handlers, validation and close/reset behavior preserved verbatim; onOpenChange mirrors the old Cancel/X reset. Removed now-unused X imports; replaced a stray em-dash with a middot in the link-member subtitle.
This commit is contained in:
@@ -4,6 +4,7 @@ import { useState } from 'react'
|
||||
import { Plus, X, Pencil, Contact as ContactIcon } from 'lucide-react'
|
||||
import { createContact, updateContact, deleteContact } from '@/lib/actions'
|
||||
import { useConfirm } from '@/components/ui/confirm'
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'
|
||||
import type { Tables } from '@/types/database'
|
||||
import { PageTitle } from '@/components/ui/page-title'
|
||||
import { Empty, EmptyHeader, EmptyMedia, EmptyTitle, EmptyContent } from '@/components/ui/empty'
|
||||
@@ -259,29 +260,23 @@ export function ContactsClient({ contacts: initialContacts }: { contacts: Contac
|
||||
)}
|
||||
</div>
|
||||
|
||||
{showCreate && (
|
||||
<div className="fixed inset-0 bg-black/60 flex items-center justify-center z-50 p-4">
|
||||
<div className="bg-card border border-border rounded-lg w-full max-w-lg">
|
||||
<div className="flex items-center justify-between px-6 py-4 border-b border-border">
|
||||
<h2 className="font-sans text-base font-semibold text-foreground">Add Contact</h2>
|
||||
<button onClick={() => { setShowCreate(false); resetForm() }} className="text-muted-foreground hover:text-foreground"><X className="w-4 h-4" /></button>
|
||||
</div>
|
||||
<Dialog open={showCreate} onOpenChange={(o) => { if (!o) { setShowCreate(false); resetForm() } }}>
|
||||
<DialogContent className="sm:max-w-lg">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Add Contact</DialogTitle>
|
||||
</DialogHeader>
|
||||
<ContactForm onSubmit={handleCreate} submitLabel="Add Contact" />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
{editContact && (
|
||||
<div className="fixed inset-0 bg-black/60 flex items-center justify-center z-50 p-4">
|
||||
<div className="bg-card border border-border rounded-lg w-full max-w-lg">
|
||||
<div className="flex items-center justify-between px-6 py-4 border-b border-border">
|
||||
<h2 className="font-sans text-base font-semibold text-foreground">Edit {editContact.name}</h2>
|
||||
<button onClick={() => { setEditContact(null); resetForm() }} className="text-muted-foreground hover:text-foreground"><X className="w-4 h-4" /></button>
|
||||
</div>
|
||||
<Dialog open={!!editContact} onOpenChange={(o) => { if (!o) { setEditContact(null); resetForm() } }}>
|
||||
<DialogContent className="sm:max-w-lg">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{editContact ? `Edit ${editContact.name}` : 'Edit Contact'}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<ContactForm onSubmit={handleEdit} submitLabel="Save Changes" />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { Plus, X, ChevronDown, Users } from 'lucide-react'
|
||||
import { Plus, ChevronDown, Users } from 'lucide-react'
|
||||
import { Empty, EmptyHeader, EmptyMedia, EmptyTitle, EmptyDescription } from '@/components/ui/empty'
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'
|
||||
import { addMember, updateMember, approveMember, removeMember, assignAreaLead } from '@/lib/actions'
|
||||
import { toast } from 'sonner'
|
||||
import { useRouter } from 'next/navigation'
|
||||
@@ -382,14 +383,12 @@ export function MembersClient({ members: initialMembers, currentRole, areaLeadRo
|
||||
</div>
|
||||
|
||||
{/* Add Member Modal */}
|
||||
{showAdd && (
|
||||
<div className="fixed inset-0 bg-black/60 flex items-center justify-center z-50 p-4">
|
||||
<div className="bg-card border border-border rounded-lg w-full max-w-lg">
|
||||
<div className="flex items-center justify-between px-6 py-4 border-b border-border">
|
||||
<h2 className="font-sans text-base font-semibold text-foreground">Add Member</h2>
|
||||
<button onClick={() => setShowAdd(false)} className="text-muted-foreground hover:text-foreground"><X className="w-4 h-4" /></button>
|
||||
</div>
|
||||
<form onSubmit={handleAdd} className="p-6 space-y-4">
|
||||
<Dialog open={showAdd} onOpenChange={(o) => { if (!o) setShowAdd(false) }}>
|
||||
<DialogContent className="sm:max-w-lg max-h-[90vh] overflow-y-auto">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Add Member</DialogTitle>
|
||||
</DialogHeader>
|
||||
<form onSubmit={handleAdd} className="space-y-4">
|
||||
<MemberFormFields />
|
||||
{error && <p className="font-mono text-xs text-red-500">{error}</p>}
|
||||
<div className="flex gap-3 pt-2">
|
||||
@@ -399,19 +398,16 @@ export function MembersClient({ members: initialMembers, currentRole, areaLeadRo
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
{/* Edit Member Modal */}
|
||||
{editMember && (
|
||||
<div className="fixed inset-0 bg-black/60 flex items-center justify-center z-50 p-4">
|
||||
<div className="bg-card border border-border rounded-lg w-full max-w-lg">
|
||||
<div className="flex items-center justify-between px-6 py-4 border-b border-border">
|
||||
<h2 className="font-sans text-base font-semibold text-foreground">Edit {editMember.display_name}</h2>
|
||||
<button onClick={() => setEditMember(null)} className="text-muted-foreground hover:text-foreground"><X className="w-4 h-4" /></button>
|
||||
</div>
|
||||
<form onSubmit={handleEdit} className="p-6 space-y-4">
|
||||
<Dialog open={!!editMember} onOpenChange={(o) => { if (!o) setEditMember(null) }}>
|
||||
<DialogContent className="sm:max-w-lg max-h-[90vh] overflow-y-auto">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Edit {editMember?.display_name}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<form onSubmit={handleEdit} className="space-y-4">
|
||||
<MemberFormFields />
|
||||
{error && <p className="font-mono text-xs text-red-500">{error}</p>}
|
||||
<div className="flex gap-3 pt-2">
|
||||
@@ -421,9 +417,8 @@ export function MembersClient({ members: initialMembers, currentRole, areaLeadRo
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { useState, useMemo } from 'react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { Plus, Search, Lock, Pin, Eye, EyeOff, Pencil, Trash2, X, ChevronDown, Users2, FileText } from 'lucide-react'
|
||||
import { Plus, Search, Lock, Pin, Eye, EyeOff, Pencil, Trash2, ChevronDown, Users2, FileText } from 'lucide-react'
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
import remarkGfm from 'remark-gfm'
|
||||
import { createKbEntry, updateKbEntry, deleteKbEntry } from '@/lib/actions'
|
||||
@@ -12,6 +12,7 @@ import { createClient } from '@/lib/supabase/client'
|
||||
import { toast } from 'sonner'
|
||||
import type { Tables, TablesInsert } from '@/types/database'
|
||||
import { PageTitle } from '@/components/ui/page-title'
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'
|
||||
import { useConfirm } from '@/components/ui/confirm'
|
||||
import { Empty, EmptyHeader, EmptyMedia, EmptyTitle, EmptyContent } from '@/components/ui/empty'
|
||||
|
||||
@@ -94,13 +95,12 @@ function KbModal({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 bg-black/60 flex items-center justify-center p-4" onClick={onClose}>
|
||||
<div className="bg-card border border-border rounded-lg w-full max-w-2xl max-h-[90vh] overflow-y-auto" onClick={e => e.stopPropagation()}>
|
||||
<div className="flex items-center justify-between px-5 py-4 border-b border-border">
|
||||
<p className="font-sans text-sm font-semibold text-foreground">{isEdit ? 'Edit Entry' : 'New KB Entry'}</p>
|
||||
<button onClick={onClose} className="text-muted-foreground hover:text-foreground"><X className="w-4 h-4" /></button>
|
||||
</div>
|
||||
<form onSubmit={handleSave} className="p-5 space-y-4">
|
||||
<Dialog open onOpenChange={(o) => { if (!o) onClose() }}>
|
||||
<DialogContent className="sm:max-w-2xl max-h-[90vh] overflow-y-auto">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="font-sans text-sm font-semibold text-foreground">{isEdit ? 'Edit Entry' : 'New KB Entry'}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<form onSubmit={handleSave} className="space-y-4">
|
||||
<div>
|
||||
<label className="font-sans text-xs font-medium text-muted-foreground block mb-1.5">Title *</label>
|
||||
<input
|
||||
@@ -182,8 +182,8 @@ function KbModal({
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -222,13 +222,12 @@ function AddSecretModal({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 bg-black/60 flex items-center justify-center p-4" onClick={onClose}>
|
||||
<div className="bg-card border border-border rounded-lg w-full max-w-md" onClick={e => e.stopPropagation()}>
|
||||
<div className="flex items-center justify-between px-5 py-4 border-b border-border">
|
||||
<p className="font-sans text-sm font-semibold text-foreground">Add Secret / Credential</p>
|
||||
<button onClick={onClose}><X className="w-4 h-4 text-muted-foreground" /></button>
|
||||
</div>
|
||||
<form onSubmit={handleSave} className="p-5 space-y-4">
|
||||
<Dialog open onOpenChange={(o) => { if (!o) onClose() }}>
|
||||
<DialogContent className="sm:max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="font-sans text-sm font-semibold text-foreground">Add Secret / Credential</DialogTitle>
|
||||
</DialogHeader>
|
||||
<form onSubmit={handleSave} className="space-y-4">
|
||||
<div>
|
||||
<label className="font-sans text-xs font-medium text-muted-foreground block mb-1.5">Label *</label>
|
||||
<input
|
||||
@@ -266,8 +265,8 @@ function AddSecretModal({
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -317,13 +316,12 @@ function AreaLeadModal({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 bg-black/60 flex items-center justify-center p-4" onClick={onClose}>
|
||||
<div className="bg-card border border-border rounded-lg w-full max-w-md" onClick={e => e.stopPropagation()}>
|
||||
<div className="flex items-center justify-between px-5 py-4 border-b border-border">
|
||||
<p className="font-sans text-sm font-semibold text-foreground">{isEdit ? 'Edit Area Lead' : 'Add Area Lead'}</p>
|
||||
<button onClick={onClose}><X className="w-4 h-4 text-muted-foreground" /></button>
|
||||
</div>
|
||||
<form onSubmit={handleSave} className="p-5 space-y-4">
|
||||
<Dialog open onOpenChange={(o) => { if (!o) onClose() }}>
|
||||
<DialogContent className="sm:max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="font-sans text-sm font-semibold text-foreground">{isEdit ? 'Edit Area Lead' : 'Add Area Lead'}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<form onSubmit={handleSave} className="space-y-4">
|
||||
<div>
|
||||
<label className="font-sans text-xs font-medium text-muted-foreground block mb-1.5">Area Name *</label>
|
||||
<input value={areaName} onChange={e => setAreaName(e.target.value)} className="w-full bg-background border border-border rounded px-3 py-2 font-sans text-sm text-foreground focus:outline-none focus:border-primary transition" placeholder="e.g. Woodshop" required />
|
||||
@@ -343,8 +341,8 @@ function AreaLeadModal({
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -507,17 +505,14 @@ function KbEntryRow({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{viewing && (
|
||||
<div className="fixed inset-0 z-50 bg-black/50 flex items-center justify-center p-4" onClick={() => setViewing(false)}>
|
||||
<div
|
||||
onClick={e => e.stopPropagation()}
|
||||
className="bg-card rounded border border-border w-full max-w-3xl max-h-[90vh] overflow-y-auto"
|
||||
>
|
||||
<div className="sticky top-0 bg-card border-b border-border px-5 py-3 flex items-start gap-3">
|
||||
<Dialog open={viewing} onOpenChange={(o) => { if (!o) setViewing(false) }}>
|
||||
<DialogContent className="sm:max-w-3xl max-h-[90vh] overflow-y-auto">
|
||||
<DialogHeader>
|
||||
<div className="flex items-start gap-3 pr-6">
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2">
|
||||
{entry.icon && <span className="text-lg">{entry.icon}</span>}
|
||||
<h2 className="font-sans text-lg font-semibold text-foreground">{entry.title}</h2>
|
||||
<DialogTitle className="font-sans text-lg font-semibold text-foreground">{entry.title}</DialogTitle>
|
||||
{entry.is_pinned && <Pin className="w-3.5 h-3.5 text-primary" />}
|
||||
</div>
|
||||
<p className="font-mono text-[10px] text-muted-foreground mt-1">
|
||||
@@ -530,11 +525,9 @@ function KbEntryRow({
|
||||
<button onClick={() => onEdit(entry)} className="text-muted-foreground hover:text-primary transition p-1" title="Edit">
|
||||
<Pencil className="w-4 h-4" />
|
||||
</button>
|
||||
<button onClick={() => setViewing(false)} className="text-muted-foreground hover:text-foreground transition p-1" title="Close">
|
||||
<X className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="px-5 py-5">
|
||||
</DialogHeader>
|
||||
<div>
|
||||
<div className="prose prose-sm prose-neutral dark:prose-invert max-w-none break-words">
|
||||
<ReactMarkdown remarkPlugins={[remarkGfm]}>{entry.content ?? ''}</ReactMarkdown>
|
||||
</div>
|
||||
@@ -547,9 +540,8 @@ function KbEntryRow({
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { X, RefreshCcw, Receipt } from 'lucide-react'
|
||||
import { RefreshCcw, Receipt } from 'lucide-react'
|
||||
import { Empty, EmptyHeader, EmptyMedia, EmptyTitle, EmptyDescription } from '@/components/ui/empty'
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from '@/components/ui/dialog'
|
||||
import { toast } from 'sonner'
|
||||
import { logCashPayment, linkPaymentToMember } from '@/lib/actions'
|
||||
import type { Tables } from '@/types/database'
|
||||
@@ -262,14 +263,12 @@ export function PaymentsClient({ payments: initialPayments, members, integration
|
||||
</div>
|
||||
|
||||
{/* Log Cash Modal */}
|
||||
{showLogCash && (
|
||||
<div className="fixed inset-0 bg-black/60 flex items-center justify-center z-50 p-4">
|
||||
<div className="bg-card border border-border rounded-lg w-full max-w-md">
|
||||
<div className="flex items-center justify-between px-6 py-4 border-b border-border">
|
||||
<h2 className="font-sans text-base font-semibold text-foreground">Log Cash Payment</h2>
|
||||
<button onClick={() => setShowLogCash(false)} className="text-muted-foreground hover:text-foreground"><X className="w-4 h-4" /></button>
|
||||
</div>
|
||||
<form onSubmit={handleLogCash} className="p-6 space-y-4">
|
||||
<Dialog open={showLogCash} onOpenChange={(o) => { if (!o) setShowLogCash(false) }}>
|
||||
<DialogContent className="sm:max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Log Cash Payment</DialogTitle>
|
||||
</DialogHeader>
|
||||
<form onSubmit={handleLogCash} className="space-y-4">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label htmlFor="cash-amount" className="font-mono text-[10px] tracking-widest text-muted-foreground uppercase block mb-1">Amount *</label>
|
||||
@@ -308,25 +307,22 @@ export function PaymentsClient({ payments: initialPayments, members, integration
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
{/* Link Member Modal */}
|
||||
<Dialog open={!!linkingPayment} onOpenChange={(o) => { if (!o) setLinkingPayment(null) }}>
|
||||
<DialogContent className="sm:max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="font-sans text-base font-semibold text-foreground">Link to Member</DialogTitle>
|
||||
{linkingPayment && (
|
||||
<div className="fixed inset-0 bg-black/60 flex items-center justify-center z-50 p-4">
|
||||
<div className="bg-card border border-border rounded-lg w-full max-w-md">
|
||||
<div className="flex items-center justify-between px-6 py-4 border-b border-border">
|
||||
<div>
|
||||
<h2 className="font-sans text-base font-semibold text-foreground">Link to Member</h2>
|
||||
<p className="font-mono text-xs text-muted-foreground mt-0.5">
|
||||
{'$'}{linkingPayment.amount} via {linkingPayment.platform?.toUpperCase()} — {linkingPayment.from_identifier}
|
||||
</p>
|
||||
</div>
|
||||
<button onClick={() => setLinkingPayment(null)} className="text-muted-foreground hover:text-foreground"><X className="w-4 h-4" /></button>
|
||||
</div>
|
||||
<div className="p-4 max-h-80 overflow-y-auto divide-y divide-border">
|
||||
{members.map(m => (
|
||||
<DialogDescription className="font-mono text-xs text-muted-foreground">
|
||||
{'$'}{linkingPayment.amount} via {linkingPayment.platform?.toUpperCase()} · {linkingPayment.from_identifier}
|
||||
</DialogDescription>
|
||||
)}
|
||||
</DialogHeader>
|
||||
<div className="max-h-80 overflow-y-auto divide-y divide-border">
|
||||
{linkingPayment && members.map(m => (
|
||||
<button
|
||||
key={m.id}
|
||||
onClick={() => handleLink(linkingPayment.id, m.id)}
|
||||
@@ -343,9 +339,8 @@ export function PaymentsClient({ payments: initialPayments, members, integration
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { useConfirm } from '@/components/ui/confirm'
|
||||
import type { Tables } from '@/types/database'
|
||||
import { PageTitle } from '@/components/ui/page-title'
|
||||
import { Empty, EmptyHeader, EmptyMedia, EmptyTitle, EmptyDescription, EmptyContent } from '@/components/ui/empty'
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'
|
||||
|
||||
type Project = Tables<'projects'>
|
||||
|
||||
@@ -164,14 +165,12 @@ export function ProjectsClient({
|
||||
)}
|
||||
</div>
|
||||
|
||||
{showCreate && (
|
||||
<div className="fixed inset-0 bg-black/60 flex items-center justify-center z-50 p-4">
|
||||
<div className="bg-card border border-border rounded-lg w-full max-w-lg">
|
||||
<div className="flex items-center justify-between px-6 py-4 border-b border-border">
|
||||
<h2 className="font-sans text-base font-semibold text-foreground">New Project</h2>
|
||||
<button onClick={() => setShowCreate(false)} className="text-muted-foreground hover:text-foreground"><X className="w-4 h-4" /></button>
|
||||
</div>
|
||||
<form onSubmit={handleCreate} className="p-6 space-y-4">
|
||||
<Dialog open={showCreate} onOpenChange={(o) => { if (!o) setShowCreate(false) }}>
|
||||
<DialogContent className="sm:max-w-lg max-h-[90vh] overflow-y-auto">
|
||||
<DialogHeader>
|
||||
<DialogTitle>New Project</DialogTitle>
|
||||
</DialogHeader>
|
||||
<form onSubmit={handleCreate} className="space-y-4">
|
||||
<div>
|
||||
<label htmlFor="project-title" className="font-mono text-[10px] tracking-widest text-muted-foreground uppercase block mb-1">Title *</label>
|
||||
<input id="project-title" type="text" required autoFocus value={form.title} onChange={e => setForm(f => ({ ...f, title: e.target.value }))}
|
||||
@@ -212,9 +211,8 @@ export function ProjectsClient({
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { X, Check, Copy, Eye, EyeOff, RotateCcw } from 'lucide-react'
|
||||
import { Check, Copy, Eye, EyeOff, RotateCcw } from 'lucide-react'
|
||||
import { updateSpaceSettings, saveIntegration, disconnectIntegration, rotateWebhookSecret, updateSpaceVisibility, createArea, updateArea, deleteArea, createTier, updateTier, deleteTier, createInvite, updateInvite, deleteInvite, createOnboardingStep, updateOnboardingStep, deleteOnboardingStep } from '@/lib/actions'
|
||||
import { toast } from 'sonner'
|
||||
import { financialVisibilities, directoryVisibilities } from '@/lib/validations'
|
||||
|
||||
import type { Tables } from '@/types/database'
|
||||
import { PageTitle } from '@/components/ui/page-title'
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'
|
||||
|
||||
type Space = Tables<'spaces'>
|
||||
type Integration = Tables<'integrations'>
|
||||
@@ -567,20 +568,18 @@ export default function SettingsClient({ space, isAdmin, integrations, currentRo
|
||||
</div>
|
||||
|
||||
{/* Integration Config Modal */}
|
||||
<Dialog open={!!editingIntegration} onOpenChange={(o) => { if (!o) setEditingIntegration(null) }}>
|
||||
<DialogContent className="sm:max-w-md">
|
||||
{editingIntegration && (
|
||||
<div className="fixed inset-0 bg-black/60 flex items-center justify-center z-50 p-4">
|
||||
<div className="bg-card border border-border rounded-lg w-full max-w-md">
|
||||
<div className="flex items-center justify-between px-6 py-4 border-b border-border">
|
||||
<div className="flex items-center gap-2">
|
||||
<>
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
<div className="w-6 h-6 flex items-center justify-center">{editingIntegration.icon}</div>
|
||||
<h2 className="font-sans text-base font-semibold text-foreground">{editingIntegration.name}</h2>
|
||||
</div>
|
||||
<button onClick={() => setEditingIntegration(null)} className="text-muted-foreground hover:text-foreground">
|
||||
<X className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
{editingIntegration.name}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<form onSubmit={handleSaveIntegration} className="p-6 space-y-4">
|
||||
<form onSubmit={handleSaveIntegration} className="space-y-4">
|
||||
{editingIntegration.fields.map(field => (
|
||||
<div key={field.key}>
|
||||
<label className="font-mono text-[10px] tracking-widest text-muted-foreground uppercase block mb-1">{field.label}</label>
|
||||
@@ -656,9 +655,10 @@ export default function SettingsClient({ space, isAdmin, integrations, currentRo
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { toast } from 'sonner'
|
||||
import type { Tables } from '@/types/database'
|
||||
import { PageTitle } from '@/components/ui/page-title'
|
||||
import { Empty, EmptyHeader, EmptyMedia, EmptyTitle, EmptyContent } from '@/components/ui/empty'
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'
|
||||
|
||||
type Task = Tables<'tasks'>
|
||||
|
||||
@@ -244,17 +245,13 @@ export function TasksClient({ tasks: initialTasks, members, currentUserId, space
|
||||
</div>
|
||||
|
||||
{/* Create Task Modal */}
|
||||
{showCreate && (
|
||||
<div className="fixed inset-0 bg-black/60 flex items-center justify-center z-50 p-4">
|
||||
<div className="bg-card border border-border rounded-lg w-full max-w-lg">
|
||||
<div className="flex items-center justify-between px-6 py-4 border-b border-border">
|
||||
<h2 className="font-sans text-base font-semibold text-foreground">New Task</h2>
|
||||
<button onClick={() => setShowCreate(false)} className="text-muted-foreground hover:text-foreground">
|
||||
<X className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
<Dialog open={showCreate} onOpenChange={(o) => { if (!o) setShowCreate(false) }}>
|
||||
<DialogContent className="sm:max-w-lg max-h-[90vh] overflow-y-auto">
|
||||
<DialogHeader>
|
||||
<DialogTitle>New Task</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<form onSubmit={handleCreate} className="p-6 space-y-4">
|
||||
<form onSubmit={handleCreate} className="space-y-4">
|
||||
<div>
|
||||
<label htmlFor="task-title" className="font-mono text-[10px] tracking-widest text-muted-foreground uppercase block mb-1">Title *</label>
|
||||
<input
|
||||
@@ -351,9 +348,8 @@ export function TasksClient({ tasks: initialTasks, members, currentUserId, space
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user