diff --git a/.kiro/specs/user-auth-and-sharing/tasks.md b/.kiro/specs/user-auth-and-sharing/tasks.md
index e30d9d4..a978f91 100644
--- a/.kiro/specs/user-auth-and-sharing/tasks.md
+++ b/.kiro/specs/user-auth-and-sharing/tasks.md
@@ -208,7 +208,7 @@
- Add invitations page
- _Requirements: 5.1, 5.4, 8.1, 8.2_
-- [-] 14. Implement profile management
+- [x] 14. Implement profile management
- Create profile page and ProfileForm component
- Implement updateProfile server action
- Create PasswordChangeForm component
@@ -236,7 +236,7 @@
- **Property 29: Profile data exposure protection**
- **Validates: Requirements 7.5**
-- [ ] 15. Update existing components for multi-user
+- [x] 15. Update existing components for multi-user
- Modify ListCard to show sharing indicator
- Update ListDetailView with share button and permission display
- Modify ItemForm to disable editing for viewers
diff --git a/app/list/[id]/item/[itemId]/edit/page.tsx b/app/list/[id]/item/[itemId]/edit/page.tsx
index ad4a7fe..10cb47c 100644
--- a/app/list/[id]/item/[itemId]/edit/page.tsx
+++ b/app/list/[id]/item/[itemId]/edit/page.tsx
@@ -1,5 +1,7 @@
import { notFound } from 'next/navigation'
import { getItem } from '@/lib/actions'
+import { canEditList } from '@/lib/permissions'
+import { auth } from '@/auth'
import ItemForm from '@/components/ItemForm'
import Link from 'next/link'
import { ArrowLeft } from 'lucide-react'
@@ -14,12 +16,20 @@ interface PageProps {
}
export default async function EditItemPage({ params }: PageProps) {
+ const session = await auth()
+ if (!session?.user?.id) {
+ notFound()
+ }
+
const item = await getItem(params.itemId)
if (!item || item.listId !== params.id) {
notFound()
}
+ // Check if user can edit this list
+ const canEdit = await canEditList(session.user.id, params.id)
+
return (
{/* Header */}
@@ -36,7 +46,7 @@ export default async function EditItemPage({ params }: PageProps) {
{/* Form */}
-
+
)
diff --git a/app/list/[id]/item/[itemId]/page.tsx b/app/list/[id]/item/[itemId]/page.tsx
index 498eb55..410759d 100644
--- a/app/list/[id]/item/[itemId]/page.tsx
+++ b/app/list/[id]/item/[itemId]/page.tsx
@@ -1,5 +1,7 @@
import { notFound } from 'next/navigation'
import { getItem } from '@/lib/actions'
+import { canEditList } from '@/lib/permissions'
+import { auth } from '@/auth'
import ItemDetail from '@/components/ItemDetail'
import Link from 'next/link'
import { ArrowLeft, Edit } from 'lucide-react'
@@ -16,12 +18,29 @@ interface PageProps {
}
export default async function ItemDetailPage({ params }: PageProps) {
- const item = await getItem(params.itemId)
-
- if (!item || item.listId !== params.id) {
+ const session = await auth()
+ if (!session?.user?.id) {
notFound()
}
+ const itemData = await getItem(params.itemId)
+
+ if (!itemData || itemData.listId !== params.id) {
+ notFound()
+ }
+
+ // Type assertion for the item with list relation
+ const item = itemData as typeof itemData & {
+ list: {
+ title: string
+ color: string
+ ownerId: string
+ }
+ }
+
+ // Check if user can edit this list
+ const canEdit = await canEditList(session.user.id, params.id)
+
return (
{/* Header Actions */}
@@ -35,21 +54,40 @@ export default async function ItemDetailPage({ params }: PageProps) {
-
-
-
- Edit
-
-
+ {canEdit && (
+ <>
+
+
+
+ Edit
+
+
+ >
+ )}
{/* Item Detail */}
-
+
)
diff --git a/app/list/[id]/page.tsx b/app/list/[id]/page.tsx
index 0637101..3c9dd09 100644
--- a/app/list/[id]/page.tsx
+++ b/app/list/[id]/page.tsx
@@ -1,4 +1,6 @@
import { getListWithItems } from '@/lib/actions'
+import { getPermission } from '@/lib/permissions'
+import { auth } from '@/auth'
import { notFound } from 'next/navigation'
import Link from 'next/link'
import { ArrowLeft } from 'lucide-react'
@@ -8,12 +10,25 @@ import ListDetailView from '@/components/ListDetailView'
export const dynamic = 'force-dynamic'
export default async function ListDetailPage({ params }: { params: { id: string } }) {
+ const session = await auth()
+ if (!session?.user?.id) {
+ notFound()
+ }
+
const list = await getListWithItems(params.id)
if (!list) {
notFound()
}
+ // Get user's permission for this list
+ const permission = await getPermission(session.user.id, params.id)
+ if (!permission) {
+ notFound()
+ }
+
+ const canShare = permission === 'owner'
+ const canEdit = permission === 'owner' || permission === 'editor'
const completedItems = list.items.filter(item => item.completed)
return (
@@ -41,13 +56,28 @@ export default async function ListDetailPage({ params }: { params: { id: string
- {/* Inline Item Creation */}
-
-
-
+ {/* Inline Item Creation - only show if user can edit */}
+ {canEdit && (
+
+
+
+ )}
{/* Search, Filter, and Items List */}
-
+ ({
+ ...c,
+ permission: c.permission as 'editor' | 'viewer'
+ }))}
+ />
)
}
diff --git a/components/ItemForm.tsx b/components/ItemForm.tsx
index 1fbaf32..2a58988 100644
--- a/components/ItemForm.tsx
+++ b/components/ItemForm.tsx
@@ -26,9 +26,10 @@ interface ItemFormProps {
notes: string | null
}
mode?: 'create' | 'edit'
+ canEdit?: boolean
}
-export default function ItemForm({ listId, item, mode = 'edit' }: ItemFormProps) {
+export default function ItemForm({ listId, item, mode = 'edit', canEdit = true }: ItemFormProps) {
const router = useRouter()
const { showToast } = useToast()
const [isSubmitting, setIsSubmitting] = useState(false)
@@ -157,6 +158,11 @@ export default function ItemForm({ listId, item, mode = 'edit' }: ItemFormProps)
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
+ if (!canEdit) {
+ showToast('You do not have permission to edit this item', 'error')
+ return
+ }
+
if (!validateForm()) {
showToast('Please fix validation errors', 'error')
return
@@ -238,6 +244,14 @@ export default function ItemForm({ listId, item, mode = 'edit' }: ItemFormProps)
return (
)
diff --git a/components/ListCard.tsx b/components/ListCard.tsx
index b3cb0dc..ad7a2d3 100644
--- a/components/ListCard.tsx
+++ b/components/ListCard.tsx
@@ -2,7 +2,7 @@
import { memo, useState } from 'react'
import Link from 'next/link'
-import { Edit2 } from 'lucide-react'
+import { Edit2, Users } from 'lucide-react'
import Modal from './Modal'
import ListForm from './ListForm'
import DeleteListButton from './DeleteListButton'
@@ -15,6 +15,11 @@ interface List {
id: string
completed: boolean
}>
+ collaborators?: Array<{
+ id: string
+ userId: string
+ permission: string
+ }>
}
function ListCard({ list }: { list: List }) {
@@ -22,6 +27,7 @@ function ListCard({ list }: { list: List }) {
const completedCount = list.items.filter(item => item.completed).length
const totalCount = list.items.length
const progress = totalCount > 0 ? (completedCount / totalCount) * 100 : 0
+ const hasCollaborators = list.collaborators && list.collaborators.length > 0
const handleEditClick = (e: React.MouseEvent) => {
e.preventDefault()
@@ -45,6 +51,12 @@ function ListCard({ list }: { list: List }) {
style={{ backgroundColor: list.color }}
/>
{list.title}
+ {hasCollaborators && list.collaborators && (
+
+
+ {list.collaborators.length}
+
+ )}