feat: add project name editing, remove address fields from settings
- Add updateProjectName server action - Project settings dialog now shows editable name field - Removed address editing section from settings (per Wassim's request) - Privacy toggle and delete project remain
This commit is contained in:
@@ -10,7 +10,7 @@ import {
|
||||
DialogTitle,
|
||||
} from '@/components/ui/primitives/dialog'
|
||||
import { Switch } from '@/components/ui/primitives/switch'
|
||||
import { updateProjectAddress, updateProjectPrivacy, deleteProject } from '../lib/projects/actions'
|
||||
import { updateProjectName, updateProjectPrivacy, deleteProject } from '../lib/projects/actions'
|
||||
import type { Project } from '../lib/projects/types'
|
||||
|
||||
interface ProjectSettingsDialogProps {
|
||||
@@ -30,19 +30,23 @@ export function ProjectSettingsDialog({
|
||||
}: ProjectSettingsDialogProps) {
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [isDeleting, setIsDeleting] = useState(false)
|
||||
const [name, setName] = useState(project.name || '')
|
||||
const [isPrivate, setIsPrivate] = useState(project.is_private)
|
||||
const [address, setAddress] = useState({
|
||||
street_number: project.address?.street_number || '',
|
||||
route: project.address?.route || '',
|
||||
city: project.address?.city || '',
|
||||
state: project.address?.state || '',
|
||||
postal_code: project.address?.postal_code || '',
|
||||
country: project.address?.country || 'US',
|
||||
})
|
||||
|
||||
const handleSave = async () => {
|
||||
setLoading(true)
|
||||
try {
|
||||
// Update name if changed
|
||||
const trimmedName = name.trim()
|
||||
if (trimmedName && trimmedName !== (project.name || '')) {
|
||||
const nameResult = await updateProjectName(project.id, trimmedName)
|
||||
if (!nameResult.success) {
|
||||
alert(`Failed to update name: ${nameResult.error}`)
|
||||
setLoading(false)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Update privacy if changed
|
||||
if (isPrivate !== project.is_private) {
|
||||
const privacyResult = await updateProjectPrivacy(project.id, isPrivate)
|
||||
@@ -53,26 +57,6 @@ export function ProjectSettingsDialog({
|
||||
}
|
||||
}
|
||||
|
||||
// Update address if changed and project has an address
|
||||
if (project.address) {
|
||||
const addressChanged =
|
||||
address.street_number !== (project.address.street_number || '') ||
|
||||
address.route !== (project.address.route || '') ||
|
||||
address.city !== (project.address.city || '') ||
|
||||
address.state !== (project.address.state || '') ||
|
||||
address.postal_code !== (project.address.postal_code || '') ||
|
||||
address.country !== (project.address.country || 'US')
|
||||
|
||||
if (addressChanged) {
|
||||
const addressResult = await updateProjectAddress(project.id, address)
|
||||
if (!addressResult.success) {
|
||||
alert(`Failed to update address: ${addressResult.error}`)
|
||||
setLoading(false)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onUpdate?.()
|
||||
onOpenChange(false)
|
||||
} catch (error) {
|
||||
@@ -108,10 +92,26 @@ export function ProjectSettingsDialog({
|
||||
<DialogContent className="sm:max-w-[500px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Project Settings</DialogTitle>
|
||||
<DialogDescription>Update project address and privacy settings</DialogDescription>
|
||||
<DialogDescription>Update project name and privacy settings</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="space-y-6 py-4">
|
||||
{/* Project Name */}
|
||||
<div>
|
||||
<label htmlFor="project-name" className="font-medium text-sm">
|
||||
Project Name
|
||||
</label>
|
||||
<input
|
||||
id="project-name"
|
||||
type="text"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
placeholder="My Project"
|
||||
className="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary"
|
||||
disabled={loading}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Privacy Toggle */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
@@ -126,83 +126,6 @@ export function ProjectSettingsDialog({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Address Fields */}
|
||||
<div className="space-y-4">
|
||||
<h3 className="font-medium">Address</h3>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="text-sm font-medium">Street Number</label>
|
||||
<input
|
||||
type="text"
|
||||
value={address.street_number}
|
||||
onChange={(e) => setAddress({ ...address, street_number: e.target.value })}
|
||||
className="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 text-sm"
|
||||
placeholder="123"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium">Street</label>
|
||||
<input
|
||||
type="text"
|
||||
value={address.route}
|
||||
onChange={(e) => setAddress({ ...address, route: e.target.value })}
|
||||
className="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 text-sm"
|
||||
placeholder="Main St"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="text-sm font-medium">City</label>
|
||||
<input
|
||||
type="text"
|
||||
value={address.city}
|
||||
onChange={(e) => setAddress({ ...address, city: e.target.value })}
|
||||
className="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 text-sm"
|
||||
placeholder="San Francisco"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium">State</label>
|
||||
<input
|
||||
type="text"
|
||||
value={address.state}
|
||||
onChange={(e) => setAddress({ ...address, state: e.target.value })}
|
||||
className="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 text-sm"
|
||||
placeholder="CA"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="text-sm font-medium">Postal Code</label>
|
||||
<input
|
||||
type="text"
|
||||
value={address.postal_code}
|
||||
onChange={(e) => setAddress({ ...address, postal_code: e.target.value })}
|
||||
className="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 text-sm"
|
||||
placeholder="94102"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium">Country</label>
|
||||
<input
|
||||
type="text"
|
||||
value={address.country}
|
||||
onChange={(e) => setAddress({ ...address, country: e.target.value })}
|
||||
className="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 text-sm"
|
||||
placeholder="US"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Danger Zone */}
|
||||
<div className="border-t border-border pt-6">
|
||||
<h3 className="font-medium text-destructive mb-2">Danger Zone</h3>
|
||||
|
||||
@@ -574,6 +574,71 @@ export async function updateProjectPrivacy(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update project name
|
||||
*/
|
||||
export async function updateProjectName(
|
||||
projectId: string,
|
||||
name: string,
|
||||
): Promise<ActionResult> {
|
||||
try {
|
||||
const session = await getSession()
|
||||
|
||||
if (!session?.user) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Not authenticated',
|
||||
}
|
||||
}
|
||||
|
||||
if (!name.trim()) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Project name cannot be empty',
|
||||
}
|
||||
}
|
||||
|
||||
const supabase = await createServerSupabaseClient()
|
||||
|
||||
// Verify ownership
|
||||
const { data: project } = await supabase
|
||||
.from('projects')
|
||||
.select('owner_id')
|
||||
.eq('id', projectId)
|
||||
.single()
|
||||
|
||||
if ((project as any)?.owner_id !== session.user.id) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Unauthorized',
|
||||
}
|
||||
}
|
||||
|
||||
// Update name
|
||||
const { error } = await (supabase
|
||||
.from('projects') as any)
|
||||
.update({ name: name.trim() })
|
||||
.eq('id', projectId)
|
||||
|
||||
if (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error.message,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Project name updated',
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Failed to update project name',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update project address
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user