-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
🔄 Merge pull request #12 from steeeee0223/feature/worxpace
Feature/worxpace: implement sidebar CRUD actions
- Loading branch information
Showing
19 changed files
with
534 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
"use server"; | ||
|
||
import { revalidatePath } from "next/cache"; | ||
|
||
import type { Document } from "@acme/prisma"; | ||
import { | ||
createSafeAction, | ||
type ActionHandler, | ||
type Modified, | ||
} from "@acme/ui/lib"; | ||
import { DeleteDocument, type DeleteDocumentInput } from "@acme/validators"; | ||
|
||
import { archive, createAuditLog, fetchClient, UnauthorizedError } from "~/lib"; | ||
|
||
const handler: ActionHandler<DeleteDocumentInput, Modified<Document>> = async ( | ||
data, | ||
) => { | ||
let result; | ||
|
||
try { | ||
const { userId, orgId } = fetchClient(); | ||
result = await archive({ ...data, userId, orgId }); | ||
/** Activity Log */ | ||
await createAuditLog( | ||
{ title: result.item.title, entityId: data.id, type: "DOCUMENT" }, | ||
"DELETE", | ||
); | ||
} catch (error) { | ||
if (error instanceof UnauthorizedError) return { error: "Unauthorized" }; | ||
console.log(`ERROR`, error); | ||
return { error: "Failed to archive document." }; | ||
} | ||
|
||
revalidatePath(`/documents/${data.id}`); | ||
return { data: result }; | ||
}; | ||
|
||
export const archiveDocument = createSafeAction(DeleteDocument, handler); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
"use server"; | ||
|
||
import { revalidatePath } from "next/cache"; | ||
|
||
import type { Document } from "@acme/prisma"; | ||
import { | ||
createSafeAction, | ||
type ActionHandler, | ||
type Modified, | ||
} from "@acme/ui/lib"; | ||
import { DeleteDocument, type DeleteDocumentInput } from "@acme/validators"; | ||
|
||
import { createAuditLog, fetchClient, remove, UnauthorizedError } from "~/lib"; | ||
|
||
const handler: ActionHandler<DeleteDocumentInput, Modified<Document>> = async ( | ||
data, | ||
) => { | ||
let result; | ||
|
||
try { | ||
const { userId, orgId } = fetchClient(); | ||
result = await remove({ userId, orgId, ...data }); | ||
/** Activity Log */ | ||
await createAuditLog( | ||
{ | ||
title: result.item.title, | ||
entityId: data.id, | ||
type: "DOCUMENT", | ||
}, | ||
"DELETE", | ||
); | ||
} catch (error) { | ||
if (error instanceof UnauthorizedError) return { error: "Unauthorized" }; | ||
console.log(`ERROR`, error); | ||
return { error: "Failed to delete document." }; | ||
} | ||
|
||
revalidatePath(`/documents`); | ||
return { data: result }; | ||
}; | ||
|
||
export const deleteDocument = createSafeAction(DeleteDocument, handler); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,4 @@ | ||
export * from "./create-document" | ||
export * from "./create-document"; | ||
export * from "./archive-document"; | ||
export * from "./restore-document"; | ||
export * from "./delete-document"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
"use server"; | ||
|
||
import { revalidatePath } from "next/cache"; | ||
|
||
import type { Document } from "@acme/prisma"; | ||
import { | ||
createSafeAction, | ||
type ActionHandler, | ||
type Modified, | ||
} from "@acme/ui/lib"; | ||
import { DeleteDocument, type DeleteDocumentInput } from "@acme/validators"; | ||
|
||
import { createAuditLog, fetchClient, restore, UnauthorizedError } from "~/lib"; | ||
|
||
const handler: ActionHandler<DeleteDocumentInput, Modified<Document>> = async ( | ||
data, | ||
) => { | ||
let result; | ||
|
||
try { | ||
const { userId, orgId } = fetchClient(); | ||
result = await restore({ ...data, userId, orgId }); | ||
/** Activity Log */ | ||
await createAuditLog( | ||
{ | ||
title: result.item.title, | ||
entityId: data.id, | ||
type: "DOCUMENT", | ||
}, | ||
"UPDATE", | ||
); | ||
} catch (error) { | ||
if (error instanceof UnauthorizedError) return { error: "Unauthorized" }; | ||
console.log(`ERROR`, error); | ||
return { error: "Failed to restore document." }; | ||
} | ||
|
||
revalidatePath(`/documents/${data.id}`); | ||
return { data: result }; | ||
}; | ||
|
||
export const restoreDocument = createSafeAction(DeleteDocument, handler); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
118 changes: 118 additions & 0 deletions
118
apps/worxpace/src/app/(platform)/(tools)/_components/trash-box.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
/* eslint-disable jsx-a11y/interactive-supports-focus */ | ||
/* eslint-disable jsx-a11y/click-events-have-key-events */ | ||
"use client"; | ||
|
||
import { MouseEvent, useState } from "react"; | ||
import { useParams, useRouter } from "next/navigation"; | ||
import { Search, Trash, Undo } from "lucide-react"; | ||
import { toast } from "sonner"; | ||
|
||
import { Input, Spinner, useTree, useTreeAction } from "@acme/ui/components"; | ||
import { useAction } from "@acme/ui/hooks"; | ||
import { cn } from "@acme/ui/lib"; | ||
|
||
import { deleteDocument, restoreDocument } from "~/actions"; | ||
import { ConfirmModal } from "~/components"; | ||
import { theme } from "~/constants/theme"; | ||
|
||
const TrashBox = () => { | ||
const router = useRouter(); | ||
const params = useParams(); | ||
/** Tree */ | ||
const { archivedItems: archivedDocs } = useTree(); | ||
const [search, setSearch] = useState(""); | ||
const filteredDocuments = archivedDocs.filter(({ title }) => | ||
title.toLowerCase().includes(search.toLowerCase()), | ||
); | ||
/** Action */ | ||
const { dispatch } = useTreeAction(); | ||
const onClick = (documentId: string) => | ||
router.push(`/documents/${documentId}`); | ||
const onError = (e: string) => toast.error(e); | ||
/** Action: Restore */ | ||
const { execute: restore } = useAction(restoreDocument, { | ||
onSuccess: (data) => { | ||
dispatch({ type: "restore", payload: data }); | ||
toast.success(`Restored document "${data.item.title}"`); | ||
}, | ||
onError, | ||
}); | ||
const onRestore = (e: MouseEvent<HTMLDivElement>, documentId: string) => { | ||
e.stopPropagation(); | ||
restore({ id: documentId }) | ||
.then(() => console.log(`processing restore`)) | ||
.catch((e) => console.log(e)); | ||
}; | ||
/** Action: Remove */ | ||
const { execute: remove } = useAction(deleteDocument, { | ||
onSuccess: (data) => { | ||
dispatch({ type: "delete", payload: data.ids }); | ||
toast.success(`Deleted document "${data.item.title}"`); | ||
if (params.documentId === data.item.id) | ||
console.log(`redirect to user/org page`); | ||
// router.push(`/documents`); | ||
}, | ||
onError, | ||
}); | ||
|
||
if (archivedDocs === undefined) | ||
return ( | ||
<div className={cn(theme.flex.center, "h-full justify-center p-4")}> | ||
<Spinner size="lg" /> | ||
</div> | ||
); | ||
return ( | ||
<div className="text-sm"> | ||
<div className={cn(theme.flex.gap1, "p-2")}> | ||
<Search className={cn(theme.size.icon, "mr-2")} /> | ||
<Input | ||
value={search} | ||
onChange={(e) => setSearch(e.target.value)} | ||
className="h-7 bg-secondary px-2 focus-visible:ring-transparent" | ||
placeholder="Filter by page title..." | ||
/> | ||
</div> | ||
<div className="mt-2 px-1 pb-1"> | ||
<p className="hidden pb-2 text-center text-xs text-muted-foreground last:block"> | ||
No documents found. | ||
</p> | ||
{filteredDocuments?.map(({ id, title }) => ( | ||
<div | ||
key={id} | ||
role="button" | ||
onClick={() => onClick(id)} | ||
className={cn( | ||
theme.flex.center, | ||
"w-full justify-between rounded-sm text-sm text-primary hover:bg-primary/5", | ||
)} | ||
> | ||
<span className="truncate pl-2">{title}</span> | ||
<div className={cn(theme.flex.gap1, "p-1")}> | ||
<div | ||
onClick={(e) => onRestore(e, id)} | ||
role="button" | ||
className={cn(theme.bg.hover, "rounded-sm p-1")} | ||
> | ||
<Undo | ||
className={cn(theme.size.icon, "text-muted-foreground")} | ||
/> | ||
</div> | ||
<ConfirmModal onConfirm={() => remove({ id })}> | ||
<div | ||
role="button" | ||
className={cn(theme.bg.hover, "rounded-sm p-1")} | ||
> | ||
<Trash | ||
className={cn(theme.size.icon, "text-muted-foreground")} | ||
/> | ||
</div> | ||
</ConfirmModal> | ||
</div> | ||
</div> | ||
))} | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default TrashBox; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.