Skip to content

Commit a72f2f1

Browse files
committed
feat(drafts): implement draft list
1 parent 8077bbb commit a72f2f1

23 files changed

Lines changed: 211 additions & 36 deletions

File tree

src/app/(private)/review/page.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import { ErrorMessage } from "@/components/presentation/error-message";
2-
import { ApplicationError } from "@/config/enums";
1+
import { ReviewList } from "@/features/review";
32

43
export default function ReviewPage() {
5-
return <ErrorMessage type={ApplicationError.NotImplemented} />;
4+
return <ReviewList />;
65
}

src/features/abstract-resource-form/components/arf-controller.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { toast } from "sonner";
99

1010
import { ReturnButton } from "@/components/presentation/return-button";
1111
import { Form } from "@/components/ui/form";
12+
import { isSolvroAdmin, useAuthentication } from "@/features/authentication";
1213
import { fetchMutation, useMutationWrapper } from "@/features/backend";
1314
import type { ModifyResourceResponse } from "@/features/backend/types";
1415
import { declineNoun } from "@/features/polish";
@@ -64,6 +65,7 @@ export function ArfController<T extends Resource>({
6465
const schema = RESOURCE_SCHEMAS[resource];
6566
const router = useRouter();
6667
const relationContext = useArfRelation();
68+
const { user } = useAuthentication();
6769
const form = useForm<ResourceFormValues<T>>({
6870
// Maybe try extracting the id from the defaultValues and passing it as an editedResourceId prop
6971
resolver: zodResolver(schema) as Resolver<ResourceFormValues<T>>,
@@ -109,8 +111,9 @@ export function ArfController<T extends Resource>({
109111
ResourceFormValues<T>
110112
>(mutationKey, async (body) => {
111113
const response = await fetchMutation<ModifyResourceResponse<T>>(endpoint, {
112-
body,
114+
body: { ...body, createdByUserId: user?.id },
113115
resource,
116+
draft: !isSolvroAdmin(user),
114117
...mutationOptions,
115118
});
116119
const wasCreated = mutationOptions.method === "POST";

src/features/abstract-resource-list/components/toggle-status-button.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
"use client";
2+
13
import { useQueryClient } from "@tanstack/react-query";
24
import { Archive, ArchiveRestore } from "lucide-react";
35
import { toast } from "sonner";
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export * from "./components/abstract-resource-list";
2+
export * from "./components/arl-item";
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const SOLVRO_ADMINS_ONLY = ["solvro_admin"] as const;
2+
export const ADMINS_ONLY = ["admin", ...SOLVRO_ADMINS_ONLY] as const;
3+
export const ANY_AUTHENTICATED_ROLE = ["user", ...ADMINS_ONLY] as const;

src/features/authentication/data/route-permissions.ts

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,40 +4,42 @@ import { Resource } from "@/features/resources";
44
import type { RoutableResource } from "@/features/resources/types";
55
import type { RecordIntersection } from "@/types/helpers";
66

7-
const SOLVRO_ADMINS_ONLY = ["solvro_admin"] as const;
8-
const ADMINS_ONLY = ["admin", ...SOLVRO_ADMINS_ONLY] as const;
9-
const ANY_AUTHENTICATED_ROLE = ["user", ...ADMINS_ONLY] as const;
7+
import {
8+
ADMINS_ONLY,
9+
ANY_AUTHENTICATED_ROLE,
10+
SOLVRO_ADMINS_ONLY,
11+
} from "./permissions";
1012

1113
export const ROUTE_PERMISSIONS = {
1214
"/": ANY_AUTHENTICATED_ROLE,
13-
"/review": SOLVRO_ADMINS_ONLY,
14-
[`/${Resource.AboutUs}`]: ANY_AUTHENTICATED_ROLE,
15-
[`/${Resource.AboutUsLinks}`]: ANY_AUTHENTICATED_ROLE,
16-
[`/${Resource.AcademicSemesters}`]: ANY_AUTHENTICATED_ROLE,
17-
[`/${Resource.Aeds}`]: ANY_AUTHENTICATED_ROLE,
18-
[`/${Resource.Banners}`]: ANY_AUTHENTICATED_ROLE,
19-
[`/${Resource.BicycleShowers}`]: ANY_AUTHENTICATED_ROLE,
20-
[`/${Resource.Buildings}`]: ANY_AUTHENTICATED_ROLE,
21-
[`/${Resource.CalendarEvents}`]: ANY_AUTHENTICATED_ROLE,
22-
[`/${Resource.Campuses}`]: ANY_AUTHENTICATED_ROLE,
23-
[`/${Resource.Contributors}`]: ANY_AUTHENTICATED_ROLE,
24-
[`/${Resource.DaySwaps}`]: ANY_AUTHENTICATED_ROLE,
25-
[`/${Resource.Departments}`]: ANY_AUTHENTICATED_ROLE,
26-
[`/${Resource.FoodSpots}`]: ANY_AUTHENTICATED_ROLE,
15+
"/review": ADMINS_ONLY,
16+
[`/${Resource.AboutUs}`]: SOLVRO_ADMINS_ONLY,
17+
[`/${Resource.AboutUsLinks}`]: SOLVRO_ADMINS_ONLY,
18+
[`/${Resource.AcademicSemesters}`]: SOLVRO_ADMINS_ONLY,
19+
[`/${Resource.Aeds}`]: SOLVRO_ADMINS_ONLY,
20+
[`/${Resource.Banners}`]: SOLVRO_ADMINS_ONLY,
21+
[`/${Resource.BicycleShowers}`]: SOLVRO_ADMINS_ONLY,
22+
[`/${Resource.Buildings}`]: SOLVRO_ADMINS_ONLY,
23+
[`/${Resource.CalendarEvents}`]: SOLVRO_ADMINS_ONLY,
24+
[`/${Resource.Campuses}`]: SOLVRO_ADMINS_ONLY,
25+
[`/${Resource.Contributors}`]: SOLVRO_ADMINS_ONLY,
26+
[`/${Resource.DaySwaps}`]: SOLVRO_ADMINS_ONLY,
27+
[`/${Resource.Departments}`]: SOLVRO_ADMINS_ONLY,
28+
[`/${Resource.FoodSpots}`]: SOLVRO_ADMINS_ONLY,
2729
[`/${Resource.GuideArticles}`]: ANY_AUTHENTICATED_ROLE,
28-
[`/${Resource.Holidays}`]: ANY_AUTHENTICATED_ROLE,
29-
[`/${Resource.Libraries}`]: ANY_AUTHENTICATED_ROLE,
30-
[`/${Resource.Map}`]: ANY_AUTHENTICATED_ROLE,
31-
[`/${Resource.Milestones}`]: ANY_AUTHENTICATED_ROLE,
32-
[`/${Resource.MobileConfig}`]: ANY_AUTHENTICATED_ROLE,
33-
[`/${Resource.Notifications}`]: ANY_AUTHENTICATED_ROLE,
34-
[`/${Resource.NotificationTopics}`]: ANY_AUTHENTICATED_ROLE,
35-
[`/${Resource.PinkBoxes}`]: ANY_AUTHENTICATED_ROLE,
36-
[`/${Resource.PolinkaStations}`]: ANY_AUTHENTICATED_ROLE,
37-
[`/${Resource.Roles}`]: ANY_AUTHENTICATED_ROLE,
38-
[`/${Resource.SksOpeningHours}`]: ANY_AUTHENTICATED_ROLE,
30+
[`/${Resource.Holidays}`]: SOLVRO_ADMINS_ONLY,
31+
[`/${Resource.Libraries}`]: SOLVRO_ADMINS_ONLY,
32+
[`/${Resource.Map}`]: SOLVRO_ADMINS_ONLY,
33+
[`/${Resource.Milestones}`]: SOLVRO_ADMINS_ONLY,
34+
[`/${Resource.MobileConfig}`]: SOLVRO_ADMINS_ONLY,
35+
[`/${Resource.Notifications}`]: SOLVRO_ADMINS_ONLY,
36+
[`/${Resource.NotificationTopics}`]: SOLVRO_ADMINS_ONLY,
37+
[`/${Resource.PinkBoxes}`]: SOLVRO_ADMINS_ONLY,
38+
[`/${Resource.PolinkaStations}`]: SOLVRO_ADMINS_ONLY,
39+
[`/${Resource.Roles}`]: SOLVRO_ADMINS_ONLY,
40+
[`/${Resource.SksOpeningHours}`]: SOLVRO_ADMINS_ONLY,
3941
[`/${Resource.StudentOrganizations}`]: ANY_AUTHENTICATED_ROLE,
40-
[`/${Resource.Versions}`]: ANY_AUTHENTICATED_ROLE,
42+
[`/${Resource.Versions}`]: SOLVRO_ADMINS_ONLY,
4143
} satisfies RecordIntersection<
4244
`/${RoutableResource}`,
4345
Route,

src/features/authentication/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ export * from "./constants";
66
export * from "./hooks/use-authentication";
77

88
export * from "./utils/get-user-display-name";
9+
export * from "./utils/is-solvro-admin";
910
export * from "./utils/parse-auth-cookie";
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { SOLVRO_ADMINS_ONLY } from "../data/permissions";
2+
import type { User } from "../types";
3+
import { getUserPermissions } from "./get-user-permissions";
4+
5+
export const isSolvroAdmin = (user: User | null): boolean => {
6+
const permissions = getUserPermissions(user);
7+
return SOLVRO_ADMINS_ONLY.every((role) => permissions.includes(role));
8+
};

src/features/backend/lib/create-request.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export function createRequest<T extends Resource>(
1313
{
1414
accessTokenOverride,
1515
resource,
16+
draft = false,
1617
body,
1718
includeRelations = false,
1819
...options
@@ -29,7 +30,7 @@ export function createRequest<T extends Resource>(
2930
};
3031
}
3132

32-
const endpointPrefix = getResourceEndpointPrefix(resource);
33+
const endpointPrefix = getResourceEndpointPrefix(resource, draft);
3334
let queryParameters = getRelationQueryParameters(resource, includeRelations);
3435
if (queryParameters !== "") {
3536
queryParameters = `${endpoint.includes("?") ? "&" : "?"}${queryParameters}`;

src/features/backend/types/internal.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ interface BaseRequestOptions<T extends Resource>
1010
headers?: Record<string, string>;
1111
accessTokenOverride?: string;
1212
resource?: T;
13+
draft?: boolean;
1314
}
1415
export interface QueryRequestOptions<T extends Resource>
1516
extends BaseRequestOptions<T> {

0 commit comments

Comments
 (0)