From 36cc4f1ef745a027540050ad668db3bcdc08d4b2 Mon Sep 17 00:00:00 2001
From: Brandon Eng <bce25@cornell.edu>
Date: Tue, 16 Jan 2024 22:41:46 +0700
Subject: Ability to archive groups when they’re settled up (#45)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Settled up icon on group card

* remove logs

* archived groups

* remove settled up

* remove more settled up

* recent-group-list-card

* sortGroups

* archiveGroup

* unarchiveGroup

* clean up

* more clean up

* Prettier, fix TS errors, add titles

---------

Co-authored-by: Sebastien Castiel <sebastien@castiel.me>
---
 src/app/groups/recent-group-list.tsx | 263 +++++++++++++----------------------
 1 file changed, 97 insertions(+), 166 deletions(-)

(limited to 'src/app/groups/recent-group-list.tsx')

diff --git a/src/app/groups/recent-group-list.tsx b/src/app/groups/recent-group-list.tsx
index 92a8067..5f41fdb 100644
--- a/src/app/groups/recent-group-list.tsx
+++ b/src/app/groups/recent-group-list.tsx
@@ -1,73 +1,80 @@
 'use client'
 import { getGroupsAction } from '@/app/groups/actions'
 import {
-  deleteRecentGroup,
+  RecentGroups,
+  getArchivedGroups,
   getRecentGroups,
   getStarredGroups,
-  saveRecentGroup,
-  starGroup,
-  unstarGroup,
 } from '@/app/groups/recent-groups-helpers'
 import { Button } from '@/components/ui/button'
-import {
-  DropdownMenu,
-  DropdownMenuContent,
-  DropdownMenuItem,
-  DropdownMenuTrigger,
-} from '@/components/ui/dropdown-menu'
-import { Skeleton } from '@/components/ui/skeleton'
-import { ToastAction } from '@/components/ui/toast'
-import { useToast } from '@/components/ui/use-toast'
 import { getGroups } from '@/lib/api'
-import { StarFilledIcon } from '@radix-ui/react-icons'
-import { Calendar, Loader2, MoreHorizontal, Star, Users } from 'lucide-react'
+import { Loader2 } from 'lucide-react'
 import Link from 'next/link'
-import { useRouter } from 'next/navigation'
-import { useEffect, useState } from 'react'
-import { z } from 'zod'
-
-const recentGroupsSchema = z.array(
-  z.object({
-    id: z.string().min(1),
-    name: z.string(),
-  }),
-)
-type RecentGroups = z.infer<typeof recentGroupsSchema>
+import { SetStateAction, useEffect, useState } from 'react'
+import { RecentGroupListCard } from './recent-group-list-card'
 
-type State =
+export type RecentGroupsState =
   | { status: 'pending' }
-  | { status: 'partial'; groups: RecentGroups; starredGroups: string[] }
+  | {
+      status: 'partial'
+      groups: RecentGroups
+      starredGroups: string[]
+      archivedGroups: string[]
+    }
   | {
       status: 'complete'
       groups: RecentGroups
       groupsDetails: Awaited<ReturnType<typeof getGroups>>
       starredGroups: string[]
+      archivedGroups: string[]
     }
 
-type Props = {
-  getGroupsAction: (groupIds: string[]) => ReturnType<typeof getGroups>
+function sortGroups(
+  state: RecentGroupsState & { status: 'complete' | 'partial' },
+) {
+  const starredGroupInfo = []
+  const groupInfo = []
+  const archivedGroupInfo = []
+  for (const group of state.groups) {
+    if (state.starredGroups.includes(group.id)) {
+      starredGroupInfo.push(group)
+    } else if (state.archivedGroups.includes(group.id)) {
+      archivedGroupInfo.push(group)
+    } else {
+      groupInfo.push(group)
+    }
+  }
+  return {
+    starredGroupInfo,
+    groupInfo,
+    archivedGroupInfo,
+  }
 }
 
 export function RecentGroupList() {
-  const [state, setState] = useState<State>({ status: 'pending' })
+  const [state, setState] = useState<RecentGroupsState>({ status: 'pending' })
 
   useEffect(() => {
     const groupsInStorage = getRecentGroups()
     const starredGroups = getStarredGroups()
-    setState({ status: 'partial', groups: groupsInStorage, starredGroups })
+    const archivedGroups = getArchivedGroups()
+    setState({
+      status: 'partial',
+      groups: groupsInStorage,
+      starredGroups,
+      archivedGroups,
+    })
     getGroupsAction(groupsInStorage.map((g) => g.id)).then((groupsDetails) => {
       setState({
         status: 'complete',
         groups: groupsInStorage,
         groupsDetails,
         starredGroups,
+        archivedGroups,
       })
     })
   }, [])
 
-  const router = useRouter()
-  const toast = useToast()
-
   if (state.status === 'pending') {
     return (
       <p>
@@ -91,139 +98,63 @@ export function RecentGroupList() {
     )
   }
 
+  const { starredGroupInfo, groupInfo, archivedGroupInfo } = sortGroups(state)
+
+  return (
+    <>
+      {starredGroupInfo.length > 0 && (
+        <>
+          <h2 className="mb-2">Starred groups</h2>
+          <GroupList
+            groups={starredGroupInfo}
+            state={state}
+            setState={setState}
+          />
+        </>
+      )}
+
+      {groupInfo.length > 0 && (
+        <>
+          <h2 className="mt-6 mb-2">Recent groups</h2>
+          <GroupList groups={groupInfo} state={state} setState={setState} />
+        </>
+      )}
+
+      {archivedGroupInfo.length > 0 && (
+        <>
+          <h2 className="mt-6 mb-2 opacity-50">Archived groups</h2>
+          <div className="opacity-50">
+            <GroupList
+              groups={archivedGroupInfo}
+              state={state}
+              setState={setState}
+            />
+          </div>
+        </>
+      )}
+    </>
+  )
+}
+
+function GroupList({
+  groups,
+  state,
+  setState,
+}: {
+  groups: RecentGroups
+  state: RecentGroupsState
+  setState: (state: SetStateAction<RecentGroupsState>) => void
+}) {
   return (
     <ul className="grid grid-cols-1 gap-2 sm:grid-cols-3">
-      {state.groups
-        .toSorted(
-          (first, second) =>
-            (state.starredGroups.includes(second.id) ? 2 : 0) -
-            (state.starredGroups.includes(first.id) ? 1 : 0),
-        )
-        .map((group) => {
-          const details =
-            state.status === 'complete'
-              ? state.groupsDetails.find((d) => d.id === group.id)
-              : null
-          return (
-            <li key={group.id}>
-              <Button variant="outline" className="h-fit w-full py-3" asChild>
-                <div
-                  className="text-base"
-                  onClick={() => router.push(`/groups/${group.id}`)}
-                >
-                  <div className="w-full flex flex-col gap-1">
-                    <div className="text-base flex gap-2 justify-between">
-                      <Link
-                        href={`/groups/${group.id}`}
-                        className="flex-1 overflow-hidden text-ellipsis"
-                      >
-                        {group.name}
-                      </Link>
-                      <span className="flex-shrink-0">
-                        <Button
-                          size="icon"
-                          variant="ghost"
-                          className="-my-3 -ml-3 -mr-1.5"
-                          onClick={(event) => {
-                            event.stopPropagation()
-                            if (state.starredGroups.includes(group.id)) {
-                              unstarGroup(group.id)
-                            } else {
-                              starGroup(group.id)
-                            }
-                            setState({
-                              ...state,
-                              starredGroups: getStarredGroups(),
-                            })
-                          }}
-                        >
-                          {state.starredGroups.includes(group.id) ? (
-                            <StarFilledIcon className="w-4 h-4 text-orange-400" />
-                          ) : (
-                            <Star className="w-4 h-4 text-muted-foreground" />
-                          )}
-                        </Button>
-                        <DropdownMenu>
-                          <DropdownMenuTrigger asChild>
-                            <Button
-                              size="icon"
-                              variant="ghost"
-                              className="-my-3 -mr-3 -ml-1.5"
-                            >
-                              <MoreHorizontal className="w-4 h-4" />
-                            </Button>
-                          </DropdownMenuTrigger>
-                          <DropdownMenuContent align="end">
-                            <DropdownMenuItem
-                              className="text-destructive"
-                              onClick={(event) => {
-                                event.stopPropagation()
-                                deleteRecentGroup(group)
-                                setState({
-                                  ...state,
-                                  groups: state.groups.filter(
-                                    (g) => g.id !== group.id,
-                                  ),
-                                })
-                                toast.toast({
-                                  title: 'Group has been removed',
-                                  description:
-                                    'The group was removed from your recent groups list.',
-                                  action: (
-                                    <ToastAction
-                                      altText="Undo group removal"
-                                      onClick={() => {
-                                        saveRecentGroup(group)
-                                        setState({
-                                          ...state,
-                                          groups: state.groups,
-                                        })
-                                      }}
-                                    >
-                                      Undo
-                                    </ToastAction>
-                                  ),
-                                })
-                              }}
-                            >
-                              Remove from recent groups
-                            </DropdownMenuItem>
-                          </DropdownMenuContent>
-                        </DropdownMenu>
-                      </span>
-                    </div>
-                    <div className="text-muted-foreground font-normal text-xs">
-                      {details ? (
-                        <div className="w-full flex items-center justify-between">
-                          <div className="flex items-center">
-                            <Users className="w-3 h-3 inline mr-1" />
-                            <span>{details._count.participants}</span>
-                          </div>
-                          <div className="flex items-center">
-                            <Calendar className="w-3 h-3 inline mx-1" />
-                            <span>
-                              {new Date(details.createdAt).toLocaleDateString(
-                                'en-US',
-                                {
-                                  dateStyle: 'medium',
-                                },
-                              )}
-                            </span>
-                          </div>
-                        </div>
-                      ) : (
-                        <div className="flex justify-between">
-                          <Skeleton className="h-4 w-6 rounded-full" />
-                          <Skeleton className="h-4 w-24 rounded-full" />
-                        </div>
-                      )}
-                    </div>
-                  </div>
-                </div>
-              </Button>
-            </li>
-          )
-        })}
+      {groups.map((group) => (
+        <RecentGroupListCard
+          key={group.id}
+          group={group}
+          state={state}
+          setState={setState}
+        />
+      ))}
     </ul>
   )
 }
-- 
cgit v1.3

