It's finally over

This commit is contained in:
MeowcaTheoRange 2024-04-24 13:07:27 -05:00
parent 1a1fedfa08
commit c553f9bfff
28 changed files with 1197 additions and 60 deletions

View file

@ -0,0 +1,31 @@
'use client';
import React from "react";
import { useFormik } from "formik";
import { useRouter } from "next/navigation";
export function Form({contentID}:{contentID:string}) {
const router = useRouter();
const formik = useFormik({
initialValues: {},
onSubmit: async (values) => {
const submitRequest = await fetch(`/jams/api/content/${contentID}`, {
method: "DELETE"
});
if (submitRequest.ok) {
router.push(`/jams/`);
} else {
formik.setSubmitting(false);
}
},
});
return (
<form onSubmit={formik.handleSubmit}>
<div>
<p><label htmlFor="submit">Are you sure?</label></p>
<input id="submit" name="submit" type="submit" value="Delete" />
</div>
</form>
);
}

View file

@ -0,0 +1,67 @@
import { Conditional, ConditionalNull } from "@/components/utility/Conditional";
import { MainLayout } from "@/layout/MainLayout/MainLayout";
import { db } from "@/lib/mastoauth/kysely";
import { JSONContentTable, JSONJamTable } from "@/lib/mastoauth/realtypes";
import { cookies } from "next/headers";
import { notFound, redirect } from "next/navigation";
import { Form } from "./components/form";
export default async function Home({
params
}: {
params: {
contentid: string
}
}) {
const cookieStore = cookies();
const token = cookieStore.get('token')?.value;
let existingUser;
if (token != null) {
let existingToken = await db
.selectFrom('tokens')
.where('tokens.id', '=', token)
.select('owner')
.executeTakeFirst();
if (existingToken != null) {
existingUser = await db
.selectFrom('users')
.where('users.id', '=', existingToken.owner)
.selectAll()
.executeTakeFirst();
}
}
if (existingUser == null) return redirect("/jams/");
const contentid = params.contentid;
if (contentid == null) return notFound();
// It's a JSONJamTable. I don't know why TS hates `number` => `string` conversion.
let editingContent = await db
.selectFrom('content')
.where('content.id', '=', contentid)
.selectAll()
.executeTakeFirst() as unknown as JSONContentTable;
if (editingContent == null) return notFound();
if (!existingUser.admin && editingContent.author_id != existingUser.id) return (
<MainLayout currentPage="/jams/" title="Delete Submission" subtitle="Jams" backButton>
<h1>Can't delete this submission</h1>
<p>You do not own this submission</p>
</MainLayout>
);
return (
<MainLayout currentPage="/jams/" title="Delete Submission" subtitle="Jams" backButton>
<p>Deleting <a href={`/jams/content/${editingContent.id}`}>{editingContent.name}</a></p>
<ConditionalNull condition={existingUser != null}>
<p><small>Logged in as <b>{existingUser?.username}@{existingUser?.instance}</b></small></p>
</ConditionalNull>
<Form contentID={editingContent.id} />
</MainLayout>
)
}

View file

@ -0,0 +1,31 @@
'use client';
import React from "react";
import { useFormik } from "formik";
import { useRouter } from "next/navigation";
export function Form({jamID}:{jamID:string}) {
const router = useRouter();
const formik = useFormik({
initialValues: {},
onSubmit: async (values) => {
const submitRequest = await fetch(`/jams/api/jams/${jamID}`, {
method: "DELETE"
});
if (submitRequest.ok) {
router.push(`/jams/`);
} else {
formik.setSubmitting(false);
}
},
});
return (
<form onSubmit={formik.handleSubmit}>
<div>
<p><label htmlFor="submit">Are you sure?</label></p>
<input id="submit" name="submit" type="submit" value="Delete" />
</div>
</form>
);
}

View file

@ -0,0 +1,67 @@
import { Conditional, ConditionalNull } from "@/components/utility/Conditional";
import { MainLayout } from "@/layout/MainLayout/MainLayout";
import { db } from "@/lib/mastoauth/kysely";
import { JSONContentTable, JSONJamTable } from "@/lib/mastoauth/realtypes";
import { cookies } from "next/headers";
import { notFound, redirect } from "next/navigation";
import { Form } from "./components/form";
export default async function Home({
params
}: {
params: {
jamid: string
}
}) {
const cookieStore = cookies();
const token = cookieStore.get('token')?.value;
let existingUser;
if (token != null) {
let existingToken = await db
.selectFrom('tokens')
.where('tokens.id', '=', token)
.select('owner')
.executeTakeFirst();
if (existingToken != null) {
existingUser = await db
.selectFrom('users')
.where('users.id', '=', existingToken.owner)
.selectAll()
.executeTakeFirst();
}
}
if (existingUser == null) return redirect("/jams/");
const jamid = params.jamid;
if (jamid == null) return notFound();
// It's a JSONJamTable. I don't know why TS hates `number` => `string` conversion.
let editingJam = await db
.selectFrom('jams')
.where('jams.id', '=', jamid)
.selectAll()
.executeTakeFirst() as unknown as JSONJamTable;
if (editingJam == null) return notFound();
if (!existingUser.admin && editingJam.author_id != existingUser.id) return (
<MainLayout currentPage="/jams/" title="Delete Jam" subtitle="Jams" backButton>
<h1>Can't delete this Jam</h1>
<p>You do not own this Jam</p>
</MainLayout>
);
return (
<MainLayout currentPage="/jams/" title="Delete Jam" subtitle="Jams" backButton>
<p>Deleting <a href={`/jams/content/${editingJam.id}`}>{editingJam.name}</a></p>
<ConditionalNull condition={existingUser != null}>
<p><small>Logged in as <b>{existingUser?.username}@{existingUser?.instance}</b></small></p>
</ConditionalNull>
<Form jamID={editingJam.id} />
</MainLayout>
)
}

View file

@ -0,0 +1,31 @@
'use client';
import React from "react";
import { useFormik } from "formik";
import { useRouter } from "next/navigation";
export function Form({judgementID, contentID}:{judgementID:string, contentID:string}) {
const router = useRouter();
const formik = useFormik({
initialValues: {},
onSubmit: async (values) => {
const submitRequest = await fetch(`/jams/api/content/${contentID}/judgements/${judgementID}`, {
method: "DELETE"
});
if (submitRequest.ok) {
router.push(`/jams/`);
} else {
formik.setSubmitting(false);
}
},
});
return (
<form onSubmit={formik.handleSubmit}>
<div>
<p><label htmlFor="submit">Are you sure?</label></p>
<input id="submit" name="submit" type="submit" value="Delete" />
</div>
</form>
);
}

View file

@ -0,0 +1,67 @@
import { Conditional, ConditionalNull } from "@/components/utility/Conditional";
import { MainLayout } from "@/layout/MainLayout/MainLayout";
import { db } from "@/lib/mastoauth/kysely";
import { JSONContentTable, JSONJamTable, JSONJudgementTable } from "@/lib/mastoauth/realtypes";
import { cookies } from "next/headers";
import { notFound, redirect } from "next/navigation";
import { Form } from "./components/form";
export default async function Home({
params
}: {
params: {
judgementid: string
}
}) {
const cookieStore = cookies();
const token = cookieStore.get('token')?.value;
let existingUser;
if (token != null) {
let existingToken = await db
.selectFrom('tokens')
.where('tokens.id', '=', token)
.select('owner')
.executeTakeFirst();
if (existingToken != null) {
existingUser = await db
.selectFrom('users')
.where('users.id', '=', existingToken.owner)
.selectAll()
.executeTakeFirst();
}
}
if (existingUser == null) return redirect("/jams/");
const judgementid = params.judgementid;
if (judgementid == null) return notFound();
// It's a JSONJamTable. I don't know why TS hates `number` => `string` conversion.
let editingJudgement = await db
.selectFrom('judgements')
.where('judgements.id', '=', judgementid)
.selectAll()
.executeTakeFirst() as unknown as JSONJudgementTable;
if (editingJudgement == null) return notFound();
if (!existingUser.admin && editingJudgement.author_id != existingUser.id) return (
<MainLayout currentPage="/jams/" title="Delete Judgement" subtitle="Jams" backButton>
<h1>Can't delete this judgement</h1>
<p>You did not publish this judgement</p>
</MainLayout>
);
return (
<MainLayout currentPage="/jams/" title="Delete Judgement" subtitle="Jams" backButton>
<p>Deleting "<a href={`/jams/content/${editingJudgement.content_id}?until=${editingJudgement.published}`}>{editingJudgement.content}</a>"</p>
<ConditionalNull condition={existingUser != null}>
<p><small>Logged in as <b>{existingUser?.username}@{existingUser?.instance}</b></small></p>
</ConditionalNull>
<Form judgementID={editingJudgement.id} contentID={editingJudgement.content_id} />
</MainLayout>
)
}

View file

@ -0,0 +1,87 @@
'use client';
import React from "react";
import { ErrorMessage, useFormik } from "formik";
import { useRouter } from "next/navigation";
import { ConditionalNull } from "@/components/utility/Conditional";
import { JSONContentTable } from "@/lib/mastoauth/realtypes";
export function Form({contentID, preset}:{contentID:string, preset:Partial<JSONContentTable>}) {
const router = useRouter();
const formik = useFormik({
initialValues: preset,
onSubmit: async (values) => {
const submitRequest = await fetch(`/jams/api/content/${contentID}`, {
method: "PATCH",
body: JSON.stringify(values)
});
if (submitRequest.ok) {
router.push(`/jams/content/${contentID}/`);
} else {
formik.setSubmitting(false);
if (submitRequest.status == 400) {
let errors:string[]|null = null;
try {
errors = await submitRequest.json();
} catch (err) { }; // No body. Skill issue
if (errors == null) return;
let transformedErrors:{[key:string]:string} = {};
errors.forEach((string) => transformedErrors[string] = "Something's wrong here");
formik.setErrors(transformedErrors);
} else {
formik.setErrors({
name: "Something went wrong..."
});
}
}
},
});
return (
<form onSubmit={formik.handleSubmit}>
<div>
<p><label htmlFor="name">Name</label></p>
<input
id="name"
name="name"
type="text"
onChange={formik.handleChange}
value={formik.values.name}
/>
<ConditionalNull condition={formik.touched.name != null && formik.errors.name != null}>
<p><small>Error: {formik.errors.name}</small></p>
</ConditionalNull>
</div>
<div>
<p><label htmlFor="description">Description</label></p>
<textarea
id="description"
name="description"
onChange={formik.handleChange}
value={formik.values.description}
/>
<ConditionalNull condition={formik.touched.description != null && formik.errors.description != null}>
<p><small>Error: {formik.errors.description}</small></p>
</ConditionalNull>
</div>
<div>
<p><label htmlFor="url">URL</label></p>
<input
id="url"
name="url"
type="url"
onChange={formik.handleChange}
value={formik.values.url}
/>
<ConditionalNull condition={formik.touched.url != null && formik.errors.url != null}>
<p><small>Error: {formik.errors.url}</small></p>
</ConditionalNull>
</div>
<br />
<div>
<p><label htmlFor="submit">Done?</label></p>
<input id="submit" name="submit" type="submit" />
</div>
</form>
);
}

View file

@ -0,0 +1,67 @@
import { Conditional, ConditionalNull } from "@/components/utility/Conditional";
import { MainLayout } from "@/layout/MainLayout/MainLayout";
import { db } from "@/lib/mastoauth/kysely";
import { JSONContentTable, JSONJamTable } from "@/lib/mastoauth/realtypes";
import { cookies } from "next/headers";
import { notFound, redirect } from "next/navigation";
import { Form } from "./components/form";
export default async function Home({
params
}: {
params: {
contentid: string
}
}) {
const cookieStore = cookies();
const token = cookieStore.get('token')?.value;
let existingUser;
if (token != null) {
let existingToken = await db
.selectFrom('tokens')
.where('tokens.id', '=', token)
.select('owner')
.executeTakeFirst();
if (existingToken != null) {
existingUser = await db
.selectFrom('users')
.where('users.id', '=', existingToken.owner)
.selectAll()
.executeTakeFirst();
}
}
if (existingUser == null) return redirect("/jams/");
const contentid = params.contentid;
if (contentid == null) return notFound();
// It's a JSONJamTable. I don't know why TS hates `number` => `string` conversion.
let editingContent = await db
.selectFrom('content')
.where('content.id', '=', contentid)
.select(['id','name','description','url'])
.executeTakeFirst() as unknown as JSONContentTable;
if (editingContent == null) return notFound();
if (!existingUser.admin && editingContent.author_id != existingUser.id) return (
<MainLayout currentPage="/jams/" title="Edit Submission" subtitle="Jams" backButton>
<h1>Can't edit this submission</h1>
<p>You do not own this submission</p>
</MainLayout>
);
return (
<MainLayout currentPage="/jams/" title="Edit Submission" subtitle="Jams" backButton>
<p>Editing <a href={`/jams/content/${editingContent.id}`}>{editingContent.name}</a></p>
<ConditionalNull condition={existingUser != null}>
<p><small>Logged in as <b>{existingUser?.username}@{existingUser?.instance}</b></small></p>
</ConditionalNull>
<Form contentID={editingContent.id} preset={editingContent} />
</MainLayout>
)
}

View file

@ -0,0 +1,115 @@
'use client';
import React from "react";
import { ErrorMessage, useFormik } from "formik";
import { useRouter } from "next/navigation";
import { ConditionalNull } from "@/components/utility/Conditional";
import { JSONContentTable, JSONJamTable } from "@/lib/mastoauth/realtypes";
export function Form({jamID, preset}:{jamID:string, preset:{
name: string,
description: string,
date_start: string,
date_end: string
}}) {
const correctPreset = {
...preset,
date_start: new Date(parseInt(preset.date_start)).toISOString().slice(0, -8),
date_end: new Date(parseInt(preset.date_end)).toISOString().slice(0, -8)
}
const router = useRouter();
const formik = useFormik({
initialValues: correctPreset,
onSubmit: async (values) => {
const correctValues = {
...values,
date_start: new Date(values.date_start).getTime(),
date_end: new Date(values.date_end).getTime(),
}
const submitRequest = await fetch(`/jams/api/jams/${jamID}`, {
method: "PATCH",
body: JSON.stringify(correctValues)
});
if (submitRequest.ok) {
router.push(`/jams/jam/${jamID}/`);
} else {
formik.setSubmitting(false);
if (submitRequest.status == 400) {
let errors:string[]|null = null;
try {
errors = await submitRequest.json();
} catch (err) { }; // No body. Skill issue
if (errors == null) return;
let transformedErrors:{[key:string]:string} = {};
errors.forEach((string) => transformedErrors[string] = "Something's wrong here");
formik.setErrors(transformedErrors);
} else {
formik.setErrors({
name: "Something went wrong..."
});
}
}
},
});
return (
<form onSubmit={formik.handleSubmit}>
<div>
<p><label htmlFor="name">Name</label></p>
<input
id="name"
name="name"
type="text"
onChange={formik.handleChange}
value={formik.values.name}
/>
<ConditionalNull condition={formik.touched.name != null && formik.errors.name != null}>
<p><small>Error: {formik.errors.name}</small></p>
</ConditionalNull>
</div>
<div>
<p><label htmlFor="description">Description</label></p>
<textarea
id="description"
name="description"
onChange={formik.handleChange}
value={formik.values.description}
/>
<ConditionalNull condition={formik.touched.description != null && formik.errors.description != null}>
<p><small>Error: {formik.errors.description}</small></p>
</ConditionalNull>
</div>
<div>
<p><label htmlFor="date_start">Date Start</label></p>
<input
id="date_start"
name="date_start"
type="datetime-local"
onChange={formik.handleChange}
value={formik.values.date_start}
/>
<ConditionalNull condition={formik.touched.date_start != null && formik.errors.date_start != null}>
<p><small>Error: {formik.errors.date_start}</small></p>
</ConditionalNull>
</div>
<div>
<p><label htmlFor="date_end">Date End</label></p>
<input
id="date_end"
name="date_end"
type="datetime-local"
onChange={formik.handleChange}
value={formik.values.date_end}
/>
<ConditionalNull condition={formik.touched.date_end != null && formik.errors.date_end != null}>
<p><small>Error: {formik.errors.date_end}</small></p>
</ConditionalNull>
</div>
<br />
<div>
<p><label htmlFor="submit">Done?</label></p>
<input id="submit" name="submit" type="submit" />
</div>
</form>
);
}

View file

@ -0,0 +1,67 @@
import { Conditional, ConditionalNull } from "@/components/utility/Conditional";
import { MainLayout } from "@/layout/MainLayout/MainLayout";
import { db } from "@/lib/mastoauth/kysely";
import { JSONContentTable, JSONJamTable } from "@/lib/mastoauth/realtypes";
import { cookies } from "next/headers";
import { notFound, redirect } from "next/navigation";
import { Form } from "./components/form";
export default async function Home({
params
}: {
params: {
jamid: string
}
}) {
const cookieStore = cookies();
const token = cookieStore.get('token')?.value;
let existingUser;
if (token != null) {
let existingToken = await db
.selectFrom('tokens')
.where('tokens.id', '=', token)
.select('owner')
.executeTakeFirst();
if (existingToken != null) {
existingUser = await db
.selectFrom('users')
.where('users.id', '=', existingToken.owner)
.selectAll()
.executeTakeFirst();
}
}
if (existingUser == null) return redirect("/jams/");
const jamid = params.jamid;
if (jamid == null) return notFound();
// It's a JSONJamTable. I don't know why TS hates `number` => `string` conversion.
let editingJam = await db
.selectFrom('jams')
.where('jams.id', '=', jamid)
.select(['id','name','description','date_start','date_end'])
.executeTakeFirst() as unknown as JSONJamTable;
if (editingJam == null) return notFound();
if (!existingUser.admin && editingJam.author_id != existingUser.id) return (
<MainLayout currentPage="/jams/" title="Edit Jam" subtitle="Jams" backButton>
<h1>Can't edit this Jam</h1>
<p>You do not own this Jam</p>
</MainLayout>
);
return (
<MainLayout currentPage="/jams/" title="Edit Jam" subtitle="Jams" backButton>
<p>Editing <a href={`/jams/content/${editingJam.id}`}>{editingJam.name}</a></p>
<ConditionalNull condition={existingUser != null}>
<p><small>Logged in as <b>{existingUser?.username}@{existingUser?.instance}</b></small></p>
</ConditionalNull>
<Form jamID={editingJam.id} preset={editingJam} />
</MainLayout>
)
}

View file

@ -0,0 +1,62 @@
'use client';
import React from "react";
import { ErrorMessage, useFormik } from "formik";
import { useRouter } from "next/navigation";
import { ConditionalNull } from "@/components/utility/Conditional";
import { JSONContentTable, JSONJudgementTable } from "@/lib/mastoauth/realtypes";
export function Form({judgementID, preset}:{judgementID:string, preset:JSONJudgementTable}) {
const router = useRouter();
const formik = useFormik({
initialValues: {
content: preset.content
},
onSubmit: async (values) => {
const submitRequest = await fetch(`/jams/api/content/${preset.content_id}/judgements/${judgementID}`, {
method: "PATCH",
body: JSON.stringify(values)
});
if (submitRequest.ok) {
router.push(`/jams/content/${preset.content_id}?since=${preset.published}`);
} else {
formik.setSubmitting(false);
if (submitRequest.status == 400) {
let errors:string[]|null = null;
try {
errors = await submitRequest.json();
} catch (err) { }; // No body. Skill issue
if (errors == null) return;
let transformedErrors:{[key:string]:string} = {};
errors.forEach((string) => transformedErrors[string] = "Something's wrong here");
formik.setErrors(transformedErrors);
} else {
formik.setErrors({
content: "Something went wrong..."
});
}
}
},
});
return (
<form onSubmit={formik.handleSubmit}>
<div>
<p><label htmlFor="content">Content</label></p>
<textarea
id="content"
name="content"
onChange={formik.handleChange}
value={formik.values.content}
/>
<ConditionalNull condition={formik.touched.content != null && formik.errors.content != null}>
<p><small>Error: {formik.errors.content}</small></p>
</ConditionalNull>
</div>
<div>
<p><label htmlFor="submit">Done?</label></p>
<input id="submit" name="submit" type="submit" />
</div>
</form>
);
}

View file

@ -0,0 +1,67 @@
import { Conditional, ConditionalNull } from "@/components/utility/Conditional";
import { MainLayout } from "@/layout/MainLayout/MainLayout";
import { db } from "@/lib/mastoauth/kysely";
import { JSONContentTable, JSONJamTable, JSONJudgementTable } from "@/lib/mastoauth/realtypes";
import { cookies } from "next/headers";
import { notFound, redirect } from "next/navigation";
import { Form } from "./components/form";
export default async function Home({
params
}: {
params: {
judgementid: string
}
}) {
const cookieStore = cookies();
const token = cookieStore.get('token')?.value;
let existingUser;
if (token != null) {
let existingToken = await db
.selectFrom('tokens')
.where('tokens.id', '=', token)
.select('owner')
.executeTakeFirst();
if (existingToken != null) {
existingUser = await db
.selectFrom('users')
.where('users.id', '=', existingToken.owner)
.selectAll()
.executeTakeFirst();
}
}
if (existingUser == null) return redirect("/jams/");
const judgementid = params.judgementid;
if (judgementid == null) return notFound();
// It's a JSONJamTable. I don't know why TS hates `number` => `string` conversion.
let editingJudgement = await db
.selectFrom('judgements')
.where('judgements.id', '=', judgementid)
.select(['content_id', 'content', 'published'])
.executeTakeFirst() as unknown as JSONJudgementTable;
if (editingJudgement == null) return notFound();
if (!existingUser.admin && editingJudgement.author_id != existingUser.id) return (
<MainLayout currentPage="/jams/" title="Edit Judgement" subtitle="Jams" backButton>
<h1>Can't edit this judgement</h1>
<p>You do not own this judgement</p>
</MainLayout>
);
return (
<MainLayout currentPage="/jams/" title="Edit Judgement" subtitle="Jams" backButton>
<p>Editing judgement "<a href={`/jams/content/${editingJudgement.content_id}?since=${editingJudgement.published}`}>{editingJudgement.content}</a>"</p>
<ConditionalNull condition={existingUser != null}>
<p><small>Logged in as <b>{existingUser?.username}@{existingUser?.instance}</b></small></p>
</ConditionalNull>
<Form judgementID={judgementid} preset={editingJudgement} />
</MainLayout>
)
}

View file

@ -1,36 +1,94 @@
'use client'; 'use client';
import React from "react"; import React from "react";
import { useFormik } from "formik"; import { ErrorMessage, useFormik } from "formik";
import { useRouter } from "next/navigation";
import { ConditionalNull } from "@/components/utility/Conditional";
export function Form() { export function Form({jamID}:{jamID:string}) {
const router = useRouter();
const formik = useFormik({ const formik = useFormik({
initialValues: { initialValues: {
email: '', name: '',
description: '',
url: '',
}, },
onSubmit: values => { onSubmit: async (values) => {
alert(JSON.stringify(values, null, 2)); const submitRequest = await fetch(`/jams/api/jams/${jamID}/content`, {
method: "POST",
body: JSON.stringify(values)
});
if (submitRequest.ok) {
let id:string = await submitRequest.text();
if (id == null) return;
router.push(`/jams/content/${id}/`);
} else {
formik.setSubmitting(false);
if (submitRequest.status == 400) {
let errors:string[]|null = null;
try {
errors = await submitRequest.json();
} catch (err) { }; // No body. Skill issue
if (errors == null) return;
let transformedErrors:{[key:string]:string} = {};
errors.forEach((string) => transformedErrors[string] = "Something's wrong here");
formik.setErrors(transformedErrors);
} else {
formik.setErrors({
name: "Something went wrong..."
});
}
}
}, },
}); });
return ( return (
<form onSubmit={formik.handleSubmit}> <form onSubmit={formik.handleSubmit}>
<label htmlFor="email">Email Address</label> <div>
<input <p><label htmlFor="name">Name</label></p>
id="name" <input
name="name" id="name"
type="text" name="name"
onChange={formik.handleChange} type="text"
value={formik.values.email} onChange={formik.handleChange}
/> value={formik.values.name}
<input />
id="name" <ConditionalNull condition={formik.touched.name != null && formik.errors.name != null}>
name="name" <p><small>Error: {formik.errors.name}</small></p>
type="text" </ConditionalNull>
onChange={formik.handleChange} </div>
value={formik.values.email} <div>
/> <p><label htmlFor="description">Description</label></p>
<textarea
<button type="submit">Submit</button> id="description"
name="description"
onChange={formik.handleChange}
value={formik.values.description}
/>
<ConditionalNull condition={formik.touched.description != null && formik.errors.description != null}>
<p><small>Error: {formik.errors.description}</small></p>
</ConditionalNull>
</div>
<div>
<p><label htmlFor="url">URL</label></p>
<input
id="url"
name="url"
type="url"
onChange={formik.handleChange}
value={formik.values.url}
/>
<ConditionalNull condition={formik.touched.url != null && formik.errors.url != null}>
<p><small>Error: {formik.errors.url}</small></p>
</ConditionalNull>
</div>
<br />
<div>
<p><label htmlFor="submit">Done?</label></p>
<input id="submit" name="submit" type="submit" />
</div>
</form> </form>
); );
} }

View file

@ -33,6 +33,8 @@ export default async function Home({
} }
} }
if (existingUser == null) return redirect("/jams/");
const jamid = params.jamid; const jamid = params.jamid;
if (jamid == null) return notFound(); if (jamid == null) return notFound();
@ -44,14 +46,14 @@ export default async function Home({
.selectAll() .selectAll()
.executeTakeFirst() as unknown as JSONJamTable; .executeTakeFirst() as unknown as JSONJamTable;
// const started = Date.now() >= parseInt(parentJam.date_start); if (parentJam == null) return notFound();
// const ended = Date.now() >= parseInt(parentJam.date_end);
const started = true; const started = Date.now() >= parseInt(parentJam.date_start);
const ended = false; const ended = Date.now() >= parseInt(parentJam.date_end);
if (!started || ended) return ( if (!started || ended) return (
<MainLayout currentPage="/jams/" title="New Submission" subtitle="Jams" backButton> <MainLayout currentPage="/jams/" title="New Submission" subtitle="Jams" backButton>
<h1>Can't submit to this jam</h1> <h1>Can't submit to this Jam</h1>
<ConditionalNull condition={!started}> <ConditionalNull condition={!started}>
<p>The jam starts at {new Date(parseInt(parentJam.date_start)).toLocaleString('en-us', { <p>The jam starts at {new Date(parseInt(parentJam.date_start)).toLocaleString('en-us', {
timeZoneName: "long" timeZoneName: "long"
@ -65,15 +67,13 @@ export default async function Home({
</MainLayout> </MainLayout>
); );
if (existingUser == null) return redirect("/jams/");
return ( return (
<MainLayout currentPage="/jams/" title="New Submission" subtitle="Jams" backButton> <MainLayout currentPage="/jams/" title="New Submission" subtitle="Jams" backButton>
<p>Submit something to <b>{parentJam.name}</b></p> <p>Submit something to <a href={`/jams/jam/${parentJam.id}`}>{parentJam.name}</a></p>
<ConditionalNull condition={existingUser != null}> <ConditionalNull condition={existingUser != null}>
<p><small>Logged in as <b>{existingUser?.username}@{existingUser?.instance}</b></small></p> <p><small>Logged in as <b>{existingUser?.username}@{existingUser?.instance}</b></small></p>
</ConditionalNull> </ConditionalNull>
<Form /> <Form jamID={parentJam.id} />
</MainLayout> </MainLayout>
) )
} }

View file

@ -0,0 +1,113 @@
'use client';
import React from "react";
import { ErrorMessage, useFormik } from "formik";
import { useRouter } from "next/navigation";
import { ConditionalNull } from "@/components/utility/Conditional";
export function Form() {
const router = useRouter();
const formik = useFormik({
initialValues: {
name: '',
description: '',
date_start: '',
date_end: '',
},
onSubmit: async (values) => {
const correctValues = {
...values,
date_start: new Date(values.date_start).getTime(),
date_end: new Date(values.date_end).getTime(),
}
const submitRequest = await fetch(`/jams/api/jams`, {
method: "POST",
body: JSON.stringify(correctValues)
});
if (submitRequest.ok) {
let id:string = await submitRequest.text();
if (id == null) return;
router.push(`/jams/jam/${id}/`);
} else {
formik.setSubmitting(false);
if (submitRequest.status == 400) {
let errors:string[]|null = null;
try {
errors = await submitRequest.json();
} catch (err) { }; // No body. Skill issue
if (errors == null) return;
let transformedErrors:{[key:string]:string} = {};
errors.forEach((string) => transformedErrors[string] = "Something's wrong here");
formik.setErrors(transformedErrors);
} else {
formik.setErrors({
name: "Something went wrong..."
});
}
}
},
});
return (
<form onSubmit={formik.handleSubmit}>
<div>
<p><label htmlFor="name">Name</label></p>
<input
id="name"
name="name"
type="text"
onChange={formik.handleChange}
value={formik.values.name}
/>
<ConditionalNull condition={formik.touched.name != null && formik.errors.name != null}>
<p><small>Error: {formik.errors.name}</small></p>
</ConditionalNull>
</div>
<div>
<p><label htmlFor="description">Description</label></p>
<textarea
id="description"
name="description"
onChange={formik.handleChange}
value={formik.values.description}
/>
<ConditionalNull condition={formik.touched.description != null && formik.errors.description != null}>
<p><small>Error: {formik.errors.description}</small></p>
</ConditionalNull>
</div>
<div>
<p><label htmlFor="date_start">Date Start</label></p>
<input
id="date_start"
name="date_start"
type="datetime-local"
onChange={formik.handleChange}
value={formik.values.date_start}
/>
<ConditionalNull condition={formik.touched.date_start != null && formik.errors.date_start != null}>
<p><small>Error: {formik.errors.date_start}</small></p>
</ConditionalNull>
</div>
<div>
<p><label htmlFor="date_end">Date End</label></p>
<input
id="date_end"
name="date_end"
type="datetime-local"
onChange={formik.handleChange}
value={formik.values.date_end}
/>
<ConditionalNull condition={formik.touched.date_end != null && formik.errors.date_end != null}>
<p><small>Error: {formik.errors.date_end}</small></p>
</ConditionalNull>
</div>
<br />
<div>
<p><label htmlFor="submit">Done?</label></p>
<input id="submit" name="submit" type="submit" />
</div>
</form>
);
}

View file

@ -0,0 +1,48 @@
import { Conditional, ConditionalNull } from "@/components/utility/Conditional";
import { MainLayout } from "@/layout/MainLayout/MainLayout";
import { db } from "@/lib/mastoauth/kysely";
import { JSONJamTable } from "@/lib/mastoauth/realtypes";
import { cookies } from "next/headers";
import { notFound, redirect } from "next/navigation";
import { Form } from "./components/form";
export default async function Home() {
const cookieStore = cookies();
const token = cookieStore.get('token')?.value;
let existingUser;
if (token != null) {
let existingToken = await db
.selectFrom('tokens')
.where('tokens.id', '=', token)
.select('owner')
.executeTakeFirst();
if (existingToken != null) {
existingUser = await db
.selectFrom('users')
.where('users.id', '=', existingToken.owner)
.selectAll()
.executeTakeFirst();
}
}
if (existingUser == null) return redirect("/jams/");
if (!existingUser.admin) return (
<MainLayout currentPage="/jams/" title="New Jam" subtitle="Jams" backButton>
<h1>Can't create a new Jam</h1>
<p>Non-admins cannot create Jams.</p>
</MainLayout>
);
return (
<MainLayout currentPage="/jams/" title="New Jam" subtitle="Jams" backButton>
<p>Create a new Jam.</p>
<ConditionalNull condition={existingUser != null}>
<p><small>Logged in as <b>{existingUser?.username}@{existingUser?.instance}</b></small></p>
</ConditionalNull>
<Form />
</MainLayout>
)
}

View file

@ -0,0 +1,65 @@
'use client';
import React from "react";
import { ErrorMessage, useFormik } from "formik";
import { useRouter } from "next/navigation";
import { ConditionalNull } from "@/components/utility/Conditional";
export function Form({contentID}:{contentID:string}) {
const router = useRouter();
const formik = useFormik({
initialValues: {
content: '',
},
onSubmit: async (values) => {
const submitRequest = await fetch(`/jams/api/content/${contentID}/judgements`, {
method: "POST",
body: JSON.stringify(values)
});
if (submitRequest.ok) {
let id:string = await submitRequest.text();
if (id == null) return;
router.push(`/jams/content/${contentID}/`);
} else {
formik.setSubmitting(false);
if (submitRequest.status == 400) {
let errors:string[]|null = null;
try {
errors = await submitRequest.json();
} catch (err) { }; // No body. Skill issue
if (errors == null) return;
let transformedErrors:{[key:string]:string} = {};
errors.forEach((string) => transformedErrors[string] = "Something's wrong here");
formik.setErrors(transformedErrors);
} else {
formik.setErrors({
content: "Something went wrong..."
});
}
}
},
});
return (
<form onSubmit={formik.handleSubmit}>
<div>
<p><label htmlFor="content">Content</label></p>
<textarea
id="content"
name="content"
onChange={formik.handleChange}
value={formik.values.content}
/>
<ConditionalNull condition={formik.touched.content != null && formik.errors.content != null}>
<p><small>Error: {formik.errors.content}</small></p>
</ConditionalNull>
</div>
<div>
<p><label htmlFor="submit">Done?</label></p>
<input id="submit" name="submit" type="submit" />
</div>
</form>
);
}

View file

@ -0,0 +1,60 @@
import { Conditional, ConditionalNull } from "@/components/utility/Conditional";
import { MainLayout } from "@/layout/MainLayout/MainLayout";
import { db } from "@/lib/mastoauth/kysely";
import { JSONContentTable, JSONJamTable } from "@/lib/mastoauth/realtypes";
import { cookies } from "next/headers";
import { notFound, redirect } from "next/navigation";
import { Form } from "./components/form";
export default async function Home({
params
}: {
params: {
contentid: string
}
}) {
const cookieStore = cookies();
const token = cookieStore.get('token')?.value;
let existingUser;
if (token != null) {
let existingToken = await db
.selectFrom('tokens')
.where('tokens.id', '=', token)
.select('owner')
.executeTakeFirst();
if (existingToken != null) {
existingUser = await db
.selectFrom('users')
.where('users.id', '=', existingToken.owner)
.selectAll()
.executeTakeFirst();
}
}
if (existingUser == null) return redirect("/jams/");
const contentid = params.contentid;
if (contentid == null) return notFound();
// It's a JSONJamTable. I don't know why TS hates `number` => `string` conversion.
let parentContent = await db
.selectFrom('content')
.where('content.id', '=', contentid)
.selectAll()
.executeTakeFirst() as unknown as JSONContentTable;
if (parentContent == null) return notFound();
return (
<MainLayout currentPage="/jams/" title="New Submission" subtitle="Jams" backButton>
<p>Judge <a href={`/jams/content/${parentContent.id}`}>{parentContent.name}</a></p>
<ConditionalNull condition={existingUser != null}>
<p><small>Logged in as <b>{existingUser?.username}@{existingUser?.instance}</b></small></p>
</ConditionalNull>
<Form contentID={parentContent.id} />
</MainLayout>
)
}

View file

@ -78,20 +78,20 @@ export async function PATCH(request: NextRequest, {params}: {params: {content:st
status: 401 status: 401
}); });
const id = params.content; const id = params.judgement;
if (id == null) return new Response('', { if (id == null) return new Response('', {
status: 400 status: 400
}); });
let updatingJudgement = await db let updatingJudgement = await db
.selectFrom('judgements') .selectFrom('judgements')
.where('judgements.content_id', '=', cid) .where('judgements.content_id', '=', cid)
.where('judgements.id', '=', id) .where('judgements.id', '=', id)
.select(['author_id']) .select(['author_id'])
.executeTakeFirst(); .executeTakeFirst();
console.log(updatingJudgement, existingUser) console.log(cid, id, updatingJudgement, existingUser)
if (updatingJudgement == null) return new Response('', { if (updatingJudgement == null) return new Response('', {
status: 400 status: 400
@ -193,7 +193,7 @@ export async function DELETE(request: NextRequest, {params}: {params: {content:s
status: 401 status: 401
}); });
console.log(deletingJudgement, existingUser) console.log(cid, id, deletingJudgement, existingUser)
if (deletingJudgement == null) return new Response('', { if (deletingJudgement == null) return new Response('', {
status: 400 status: 400

View file

@ -183,6 +183,14 @@ export async function DELETE(request: NextRequest, {params}: {params: {content:s
status: 401 status: 401
}); });
try {
await db
.deleteFrom('judgements')
.where('judgements.content_id', '=', id)
.execute();
} catch (err) {
}
try { try {
await db await db
.deleteFrom('content') .deleteFrom('content')

View file

@ -156,6 +156,15 @@ export async function DELETE(request: NextRequest, {params}: {params: {jam:strin
status: 401 status: 401
}); });
try {
await db
.updateTable('content')
.set('content.jam_id', '')
.where('content.jam_id', '=', id)
.execute();
} catch (err) {
}
try { try {
await db await db
.deleteFrom('jams') .deleteFrom('jams')

View file

@ -77,19 +77,21 @@ export async function POST(request: NextRequest) {
let newBody:JamTable = body; let newBody:JamTable = body;
let curBody = {
id: nanoid(21),
author_id: existingUser.id,
name: newBody.name,
description: newBody.description,
date_start: newBody.date_start,
date_end: newBody.date_end,
created: Date.now()
};
let res; let res;
try { try {
res = await db res = await db
.insertInto('jams') .insertInto('jams')
.values({ .values(curBody)
id: nanoid(21),
author_id: existingUser.id,
name: newBody.name,
description: newBody.description,
date_start: newBody.date_start,
date_end: newBody.date_end,
created: Date.now()
})
.executeTakeFirstOrThrow(); .executeTakeFirstOrThrow();
} catch (err) { } catch (err) {
return new Response('', { return new Response('', {
@ -97,7 +99,7 @@ export async function POST(request: NextRequest) {
}); });
} }
return new Response(null, { return new Response(curBody.id, {
status: 204 status: 200
}) })
} }

View file

@ -84,13 +84,18 @@ export default async function Home({
<MainLayout currentPage="/jams/" title={content.name} subtitle="Jams" backButton> <MainLayout currentPage="/jams/" title={content.name} subtitle="Jams" backButton>
<h1>{content.name}</h1> <h1>{content.name}</h1>
<p><a href={content.url} target="_blank">{content.url}</a></p> <p><a href={content.url} target="_blank">{content.url}</a></p>
<ConditionalNull condition={submittedJam != null}>
<p><small>Submitted by <a href={`/jams/user/${contentOwner.id}`}>{`${contentOwner.username}@${contentOwner.instance}`}</a> to <a href={`/jams/jam/${submittedJam.id}?until=${content.submitted}`}>{submittedJam.name}</a> - {new Date(parseInt(content.submitted)).toDateString()}</small></p> <p><small>Submitted by <a href={`/jams/user/${contentOwner.id}`}>{`${contentOwner.username}@${contentOwner.instance}`}</a> to <a href={`/jams/jam/${submittedJam.id}?until=${content.submitted}`}>{submittedJam.name}</a> - {new Date(parseInt(content.submitted)).toDateString()}</small></p>
</ConditionalNull>
<ConditionalNull condition={submittedJam == null}>
<p><small>Submitted by <a href={`/jams/user/${contentOwner.id}`}>{`${contentOwner.username}@${contentOwner.instance}`}</a> {new Date(parseInt(content.submitted)).toDateString()}</small></p>
</ConditionalNull>
<p>{content.description}</p> <p>{content.description}</p>
<ConditionalNull condition={existingUser != null}> <ConditionalNull condition={existingUser != null}>
<div> <div>
<ConditionalNull condition={existingUser?.admin || content.author_id == content.id}><p><b>You have the ability to modify this submission.</b></p></ConditionalNull> <ConditionalNull condition={existingUser?.admin || content.author_id == existingUser?.id}><p><b>You have the ability to modify this submission.</b></p></ConditionalNull>
<ConditionalNull condition={existingUser?.admin || content.author_id == content.id}><p><a href={`/jams/edit/content/${content.id}`}>Edit submission</a></p></ConditionalNull> <ConditionalNull condition={existingUser?.admin || content.author_id == existingUser?.id}><p><a href={`/jams/edit/content/${content.id}`}>Edit submission</a></p></ConditionalNull>
<ConditionalNull condition={existingUser?.admin || content.author_id == content.id}><p><a href={`/jams/delete/content/${content.id}`}>Delete submission</a></p></ConditionalNull> <ConditionalNull condition={existingUser?.admin || content.author_id == existingUser?.id}><p><a href={`/jams/delete/content/${content.id}`}>Delete submission</a></p></ConditionalNull>
<p><a href={`/jams/new/judgement/${content.id}`}>Judge this submission</a></p> <p><a href={`/jams/new/judgement/${content.id}`}>Judge this submission</a></p>
</div> </div>
</ConditionalNull> </ConditionalNull>
@ -110,7 +115,7 @@ export default async function Home({
<h2><a href={`/jams/user/${judgementOwner.id}`}>{`${judgementOwner.username}@${judgementOwner.instance}`}</a></h2> <h2><a href={`/jams/user/${judgementOwner.id}`}>{`${judgementOwner.username}@${judgementOwner.instance}`}</a></h2>
<ConditionalNull condition={existingUser != null}> <ConditionalNull condition={existingUser != null}>
<div> <div>
<ConditionalNull condition={existingUser?.admin || content.author_id == content.id}><p><a href={`/jams/edit/judgement/${judgement.id}`}>Edit judgement</a> - <a href={`/jams/delete/judgement/${judgement.id}`}>Delete judgement</a></p></ConditionalNull> <ConditionalNull condition={existingUser?.admin || judgementOwner.id == existingUser?.id}><p><a href={`/jams/edit/judgement/${judgement.id}`}>Edit judgement</a> - <a href={`/jams/delete/judgement/${judgement.id}`}>Delete judgement</a></p></ConditionalNull>
</div> </div>
</ConditionalNull> </ConditionalNull>
<p><small>Published {new Date(parseInt(judgement.published)).toDateString()}</small></p> <p><small>Published {new Date(parseInt(judgement.published)).toDateString()}</small></p>
@ -119,7 +124,7 @@ export default async function Home({
})} })}
<div className="navigation"> <div className="navigation">
<ConditionalNull condition={curPage < curDate}> <ConditionalNull condition={curPage < curDate}>
<Link href={`?until=${judgements.at(0)?.published}`}>Later</Link> <Link href={`?`}>Later</Link>
</ConditionalNull> </ConditionalNull>
{/* <ConditionalNull condition={curPage * 10 < blogs.data.total_posts}> */} {/* <ConditionalNull condition={curPage * 10 < blogs.data.total_posts}> */}
<Link href={`?until=${judgements.at(-1)?.published}`}>Earlier</Link> <Link href={`?until=${judgements.at(-1)?.published}`}>Earlier</Link>

View file

@ -108,7 +108,7 @@ export default async function Home({
})} })}
<div className="navigation"> <div className="navigation">
<ConditionalNull condition={curPage < curDate}> <ConditionalNull condition={curPage < curDate}>
<Link href={`?until=${content.at(0)?.submitted}`}>Later</Link> <Link href={`?`}>Later</Link>
</ConditionalNull> </ConditionalNull>
{/* <ConditionalNull condition={curPage * 10 < blogs.data.total_posts}> */} {/* <ConditionalNull condition={curPage * 10 < blogs.data.total_posts}> */}
<Link href={`?until=${content.at(-1)?.submitted}`}>Earlier</Link> <Link href={`?until=${content.at(-1)?.submitted}`}>Earlier</Link>

View file

@ -85,7 +85,7 @@ export default async function Home({
})} })}
<div className="navigation"> <div className="navigation">
<ConditionalNull condition={curPage < curDate}> <ConditionalNull condition={curPage < curDate}>
<Link href={`?until=${jams.at(0)?.created}`}>Later</Link> <Link href={`?`}>Later</Link>
</ConditionalNull> </ConditionalNull>
{/* <ConditionalNull condition={curPage * 10 < blogs.data.total_posts}> */} {/* <ConditionalNull condition={curPage * 10 < blogs.data.total_posts}> */}
<Link href={`?until=${jams.at(-1)?.created}`}>Earlier</Link> <Link href={`?until=${jams.at(-1)?.created}`}>Earlier</Link>

View file

@ -138,7 +138,7 @@ export default async function Home({
})} })}
{/* <div className="navigation"> {/* <div className="navigation">
<ConditionalNull condition={curPage < curDate}> <ConditionalNull condition={curPage < curDate}>
<Link href={`?until=${content.at(0)?.submitted}`}>Later</Link> <Link href={`?`}>Later</Link>
</ConditionalNull> </ConditionalNull>
<Link href={`?until=${content.at(-1)?.submitted}`}>Earlier</Link> <Link href={`?until=${content.at(-1)?.submitted}`}>Earlier</Link>
</div> */} </div> */}

View file

@ -25,8 +25,8 @@ export class MastoAuth {
async newApplication() { async newApplication() {
let formData = new FormData(); let formData = new FormData();
formData.append('client_name', 'abtmtr.link Jams'); formData.append('client_name', 'abtmtr.link Jams');
// formData.append('redirect_uris', 'https://abtmtr.link/jams/oauth/code'); formData.append('redirect_uris', 'https://abtmtr.link/jams/oauth/code');
formData.append('redirect_uris', 'http://localhost:3000/jams/oauth/code'); // formData.append('redirect_uris', 'http://localhost:3000/jams/oauth/code');
formData.append('scopes', 'read'); formData.append('scopes', 'read');
formData.append('website', 'https://abtmtr.link/jams'); formData.append('website', 'https://abtmtr.link/jams');

View file

@ -74,6 +74,16 @@ button {
transition: all 0.125s; transition: all 0.125s;
} }
input:not([type="submit"]),
textarea {
width: 100%;
font-family: monospace;
text-align: left;
}
textarea {
resize: vertical;
}
button:hover { button:hover {
opacity: 0.75; opacity: 0.75;
} }