Skip to content

Commit 9101ed1

Browse files
authored
Merge pull request #277 from fccview/develop
Lift off!
2 parents bd41f93 + 989d926 commit 9101ed1

507 files changed

Lines changed: 17710 additions & 4725 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.vscode/settings.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"i18n-ally.localesPaths": [
3+
"app/_translations"
4+
],
5+
"i18n-ally.keystyle": "nested"
6+
}

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ I would like to thank the following members for raising issues and help test/deb
258258
<a href="https://github.com/Isotop7"><img width="100" height="100" src="https://avatars.githubusercontent.com/u/8883656?v=4&size=100"><br />Isotop7</a>
259259
</td>
260260
<td align="center" valign="top" width="20%">
261-
<a href="https://github.com/bluegumcity"><img width="100" height="100" src="https://avatars.githubusercontent.com/u/142639670?v=4&size=100"><br />bluegumcity</a>
261+
<a href="https://github.com/gavdgavd"><img width="100" height="100" src="https://avatars.githubusercontent.com/u/7753641?v=4&size=100"><br />gavdgavd</a>
262262
</td>
263263
</tr>
264264
<tr>
@@ -313,6 +313,9 @@ I would like to thank the following members for raising issues and help test/deb
313313
</td>
314314
</tr>
315315
<tr>
316+
<td align="center" valign="top" width="20%">
317+
<a href="https://github.com/bluegumcity"><img width="100" height="100" src="https://avatars.githubusercontent.com/u/142639670?v=4&size=100"><br />bluegumcity</a>
318+
</td>
316319
<td align="center" valign="top" width="20%">
317320
<a href="https://github.com/4rft5"><img width="100" height="100" src="https://avatars.githubusercontent.com/u/74219775?s=100&v=4"><br />4rft5</a>
318321
</td>
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { redirect } from "next/navigation";
2+
import {
3+
getListById,
4+
getUserChecklists,
5+
} from "@/app/_server/actions/checklist";
6+
import { getCategories } from "@/app/_server/actions/category";
7+
import { getCurrentUser, canAccessAllContent } from "@/app/_server/actions/users";
8+
import { ChecklistClient } from "@/app/_components/FeatureComponents/Checklists/Parts/ChecklistClient";
9+
import { Modes } from "@/app/_types/enums";
10+
import type { Metadata } from "next";
11+
import { getMedatadaTitle } from "@/app/_server/actions/config";
12+
import { PermissionsProvider } from "@/app/_providers/PermissionsProvider";
13+
import { MetadataProvider } from "@/app/_providers/MetadataProvider";
14+
15+
interface AdminChecklistPageProps {
16+
params: {
17+
uuid: string;
18+
};
19+
}
20+
21+
export const dynamic = "force-dynamic";
22+
23+
export async function generateMetadata({
24+
params,
25+
}: AdminChecklistPageProps): Promise<Metadata> {
26+
const { uuid } = params;
27+
return getMedatadaTitle(Modes.CHECKLISTS, uuid, "Admin");
28+
}
29+
30+
export default async function AdminChecklistPage({ params }: AdminChecklistPageProps) {
31+
const { uuid } = params;
32+
const user = await getCurrentUser();
33+
const hasContentAccess = await canAccessAllContent();
34+
35+
if (!hasContentAccess) {
36+
redirect("/");
37+
}
38+
39+
const [listsResult, categoriesResult] = await Promise.all([
40+
getUserChecklists({ username: user?.username }),
41+
getCategories(Modes.CHECKLISTS),
42+
]);
43+
44+
if (!listsResult.success || !listsResult.data) {
45+
redirect("/");
46+
}
47+
48+
const checklist = await getListById(uuid);
49+
50+
if (!checklist) {
51+
redirect("/");
52+
}
53+
54+
const categories =
55+
categoriesResult.success && categoriesResult.data
56+
? categoriesResult.data
57+
: [];
58+
59+
const metadata = {
60+
id: checklist.id,
61+
uuid: checklist.uuid,
62+
title: checklist.title,
63+
category: checklist.category || "Uncategorized",
64+
owner: checklist.owner,
65+
createdAt: checklist.createdAt,
66+
updatedAt: checklist.updatedAt,
67+
type: "checklist" as const,
68+
};
69+
70+
return (
71+
<MetadataProvider metadata={metadata}>
72+
<PermissionsProvider item={checklist}>
73+
<ChecklistClient
74+
checklist={checklist}
75+
categories={categories}
76+
user={user}
77+
/>
78+
</PermissionsProvider>
79+
</MetadataProvider>
80+
);
81+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { redirect } from "next/navigation";
2+
import {
3+
CheckForNeedsMigration,
4+
getNoteById,
5+
getUserNotes,
6+
} from "@/app/_server/actions/note";
7+
import { getCurrentUser, canAccessAllContent } from "@/app/_server/actions/users";
8+
import { NoteClient } from "@/app/_components/FeatureComponents/Notes/NoteClient";
9+
import { Modes } from "@/app/_types/enums";
10+
import { getCategories } from "@/app/_server/actions/category";
11+
import type { Metadata } from "next";
12+
import { getMedatadaTitle } from "@/app/_server/actions/config";
13+
import { PermissionsProvider } from "@/app/_providers/PermissionsProvider";
14+
import { MetadataProvider } from "@/app/_providers/MetadataProvider";
15+
16+
interface AdminNotePageProps {
17+
params: {
18+
uuid: string;
19+
};
20+
}
21+
22+
export const dynamic = "force-dynamic";
23+
24+
export async function generateMetadata({
25+
params,
26+
}: AdminNotePageProps): Promise<Metadata> {
27+
const { uuid } = params;
28+
return getMedatadaTitle(Modes.NOTES, uuid, "Admin");
29+
}
30+
31+
export default async function AdminNotePage({ params }: AdminNotePageProps) {
32+
const { uuid } = params;
33+
const hasContentAccess = await canAccessAllContent();
34+
35+
if (!hasContentAccess) {
36+
redirect("/");
37+
}
38+
39+
await CheckForNeedsMigration();
40+
41+
const [docsResult, categoriesResult] = await Promise.all([
42+
getUserNotes({ isRaw: true }),
43+
getCategories(Modes.NOTES),
44+
]);
45+
46+
if (!docsResult.success || !docsResult.data) {
47+
redirect("/");
48+
}
49+
50+
const note = await getNoteById(uuid);
51+
52+
if (!note) {
53+
redirect("/");
54+
}
55+
56+
const docsCategories =
57+
categoriesResult.success && categoriesResult.data
58+
? categoriesResult.data
59+
: [];
60+
61+
const metadata = {
62+
id: note.id,
63+
uuid: note.uuid,
64+
title: note.title,
65+
category: note.category || "Uncategorized",
66+
owner: note.owner,
67+
createdAt: note.createdAt,
68+
updatedAt: note.updatedAt,
69+
type: "note" as const,
70+
};
71+
72+
return (
73+
<MetadataProvider metadata={metadata}>
74+
<PermissionsProvider item={note}>
75+
<NoteClient note={note} categories={docsCategories} />
76+
</PermissionsProvider>
77+
</MetadataProvider>
78+
);
79+
}

app/(loggedInRoutes)/admin/page.tsx

Lines changed: 0 additions & 19 deletions
This file was deleted.

app/(loggedInRoutes)/checklist/[...categoryPath]/page.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
} from "@/app/_server/actions/checklist";
66
import { getCategories } from "@/app/_server/actions/category";
77
import { getAllLists } from "@/app/_server/actions/checklist";
8-
import { getCurrentUser } from "@/app/_server/actions/users";
8+
import { getCurrentUser, canAccessAllContent } from "@/app/_server/actions/users";
99
import { ChecklistClient } from "@/app/_components/FeatureComponents/Checklists/Parts/ChecklistClient";
1010
import { Modes } from "@/app/_types/enums";
1111
import type { Metadata } from "next";
@@ -46,7 +46,7 @@ export default async function ChecklistPage({ params }: ChecklistPageProps) {
4646
: decodeCategoryPath(encodedCategoryPath);
4747
const user = await getCurrentUser();
4848
const username = user?.username || "";
49-
const isAdminUser = user?.isAdmin || false;
49+
const hasContentAccess = await canAccessAllContent();
5050

5151
const [listsResult, categoriesResult] = await Promise.all([
5252
getUserChecklists({ username }),
@@ -59,7 +59,7 @@ export default async function ChecklistPage({ params }: ChecklistPageProps) {
5959

6060
let checklist = await getListById(id, username, category);
6161

62-
if (!checklist && isAdminUser) {
62+
if (!checklist && hasContentAccess) {
6363
const allListsResult = await getAllLists();
6464
if (allListsResult.success && allListsResult.data) {
6565
checklist = allListsResult.data.find(
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { ChecklistsClient } from "@/app/_components/FeatureComponents/Checklists/ChecklistsClient";
2+
import { getCategories } from "@/app/_server/actions/category";
3+
import { getCurrentUser } from "@/app/_server/actions/users";
4+
import { Modes } from "@/app/_types/enums";
5+
6+
export default async function ChecklistsLayout({
7+
children,
8+
}: {
9+
children: React.ReactNode;
10+
}) {
11+
const [checklistCategories, user] = await Promise.all([
12+
getCategories(Modes.CHECKLISTS),
13+
getCurrentUser(),
14+
]);
15+
16+
const categories = checklistCategories.data || [];
17+
18+
return (
19+
<ChecklistsClient categories={categories} user={user}>
20+
{children}
21+
</ChecklistsClient>
22+
);
23+
}

app/(loggedInRoutes)/checklists/page.tsx

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,21 @@
11
import { getUserChecklists } from "@/app/_server/actions/checklist";
2-
import { getCategories } from "@/app/_server/actions/category";
32
import { getCurrentUser } from "@/app/_server/actions/users";
4-
import { Modes } from "@/app/_types/enums";
53
import { ChecklistsPageClient } from "@/app/_components/FeatureComponents/Checklists/ChecklistsPageClient";
64
import { Checklist } from "@/app/_types";
75

86
export const dynamic = "force-dynamic";
97

108
export default async function ChecklistsPage() {
11-
const [listsResult, categoriesResult] = await Promise.all([
9+
const [listsResult, user] = await Promise.all([
1210
getUserChecklists(),
13-
getCategories(Modes.CHECKLISTS),
11+
getCurrentUser(),
1412
]);
1513

1614
const lists = listsResult.success && listsResult.data ? listsResult.data : [];
17-
const categories =
18-
categoriesResult.success && categoriesResult.data
19-
? categoriesResult.data
20-
: [];
21-
const user = await getCurrentUser();
2215

2316
return (
2417
<ChecklistsPageClient
2518
initialLists={lists as Checklist[]}
26-
initialCategories={categories}
2719
user={user}
2820
/>
2921
);
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { redirect } from "next/navigation";
2+
import { UnifiedMarkdownRenderer } from "@/app/_components/FeatureComponents/Notes/Parts/UnifiedMarkdownRenderer";
3+
import { readFile } from "@/app/_server/actions/file";
4+
import { convertMarkdownToHtml } from "@/app/_utils/markdown-utils";
5+
import type { Metadata } from "next";
6+
import { getTranslations } from "next-intl/server";
7+
import { getHowtoGuideById, getHowtoFilePath, isValidHowtoGuide } from "@/app/_utils/howto-utils";
8+
9+
interface HowtoPageProps {
10+
params: {
11+
path: string;
12+
};
13+
}
14+
15+
export const dynamic = "force-dynamic";
16+
17+
export async function generateMetadata({
18+
params,
19+
}: HowtoPageProps): Promise<Metadata> {
20+
const { path } = params;
21+
const t = await getTranslations();
22+
const guide = getHowtoGuideById(path, t);
23+
24+
if (!guide) {
25+
return {
26+
title: "How To - jotty·page",
27+
};
28+
}
29+
30+
return {
31+
title: `${guide.name} - How To - jotty·page`,
32+
description: `Learn about ${guide.name.toLowerCase()} in jotty·page`,
33+
};
34+
}
35+
36+
export default async function HowtoPage({ params }: HowtoPageProps) {
37+
const { path } = params;
38+
const t = await getTranslations();
39+
40+
if (!isValidHowtoGuide(path, t)) {
41+
redirect("/howto/shortcuts");
42+
}
43+
44+
const guide = getHowtoGuideById(path, t);
45+
if (!guide) {
46+
redirect("/howto/shortcuts");
47+
}
48+
49+
const filePath = getHowtoFilePath(guide.filename);
50+
const markdownContent = await readFile(filePath);
51+
52+
if (!markdownContent) {
53+
return (
54+
<div>
55+
<div className="mb-6">
56+
<h1 className="text-3xl font-bold text-foreground">{guide.name}</h1>
57+
</div>
58+
<div className="prose prose-sm sm:prose-base lg:prose-lg xl:prose-2xl dark:prose-invert max-w-none">
59+
<p>{t("errors.notFound")}</p>
60+
</div>
61+
</div>
62+
);
63+
}
64+
65+
const htmlContent = convertMarkdownToHtml(markdownContent);
66+
67+
return (
68+
<div>
69+
<div className="prose prose-sm sm:prose-base lg:prose-lg xl:prose-2xl dark:prose-invert max-w-none">
70+
<UnifiedMarkdownRenderer content={htmlContent} />
71+
</div>
72+
</div>
73+
);
74+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { HowtoClient } from "@/app/_components/FeatureComponents/Howto/HowtoClient";
2+
import { getCategories } from "@/app/_server/actions/category";
3+
import { getCurrentUser } from "@/app/_server/actions/users";
4+
import { Modes } from "@/app/_types/enums";
5+
6+
export default async function HowtoLayout({
7+
children,
8+
}: {
9+
children: React.ReactNode;
10+
}) {
11+
const [checklistCategories, noteCategories, user] = await Promise.all([
12+
getCategories(Modes.CHECKLISTS),
13+
getCategories(Modes.NOTES),
14+
getCurrentUser(),
15+
]);
16+
17+
const categories = [
18+
...(checklistCategories.data || []),
19+
...(noteCategories.data || []),
20+
];
21+
22+
return (
23+
<HowtoClient categories={categories} user={user}>
24+
{children}
25+
</HowtoClient>
26+
);
27+
}

0 commit comments

Comments
 (0)