-
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.
- Loading branch information
Showing
14 changed files
with
632 additions
and
4 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,165 @@ | ||
import { | ||
type ActionArgs, | ||
type LoaderArgs, | ||
json, | ||
redirect, | ||
type HeadersArgs | ||
} from '@remix-run/node' | ||
import {invariant} from '@arcath/utils' | ||
|
||
import {getUserFromUPN, getUPNFromHeaders} from '~/lib/user.server' | ||
import {getPrisma} from '~/lib/prisma' | ||
import {log} from '~/log.server' | ||
import {createTimings} from '~/utils/timings.server' | ||
|
||
import { | ||
labelClasses, | ||
inputClasses, | ||
fieldsetClasses, | ||
labelSpanClasses, | ||
buttonClasses | ||
} from '~/lib/classes' | ||
|
||
export const loader = async ({request}: LoaderArgs) => { | ||
const {time, getHeader} = createTimings() | ||
|
||
const user = await time('getUser', 'Get User from header', () => | ||
getUserFromUPN(getUPNFromHeaders(request)) | ||
) | ||
|
||
if (!user || user.type !== 'STAFF') { | ||
throw new Response('Access Denied', {status: 403}) | ||
} | ||
|
||
return json({user}, {headers: {'Server-Timing': getHeader()}}) | ||
} | ||
|
||
export const action = async ({request}: ActionArgs) => { | ||
const {time, getHeader} = createTimings() | ||
|
||
const user = await time('getUser', 'Get User from header', () => | ||
getUserFromUPN(getUPNFromHeaders(request)) | ||
) | ||
|
||
if (!user || user.type !== 'STAFF') { | ||
throw new Response('Access Denied', {status: 403}) | ||
} | ||
|
||
const prisma = getPrisma() | ||
|
||
const formData = await request.formData() | ||
|
||
const title = formData.get('title') as string | undefined | ||
const type = formData.get('type') as | ||
| ('Info' | 'Danger' | 'Warning') | ||
| undefined | ||
const message = formData.get('message') as string | undefined | ||
const startDate = formData.get('start-date') as string | undefined | ||
const endDate = formData.get('end-date') as string | undefined | ||
let target = formData.get('target') as string | ||
const scope = formData.get('scopes') as string | undefined | ||
|
||
invariant(title) | ||
invariant(type) | ||
invariant(message) | ||
invariant(startDate) | ||
invariant(endDate) | ||
invariant(scope) | ||
|
||
if (!target) { | ||
target = '#' | ||
} | ||
|
||
const scopes = scope.split(',').map(s => s.trim().toLowerCase()) | ||
|
||
await time('createMessage', 'Create Message', () => { | ||
return prisma.infoMessage.create({ | ||
data: { | ||
title, | ||
type, | ||
message, | ||
startDate: new Date(startDate), | ||
endDate: new Date(endDate), | ||
target, | ||
scopes | ||
} | ||
}) | ||
}) | ||
|
||
await log('Message', `Created new message ${title}`, user.username) | ||
|
||
return redirect('/admin/messages', {headers: {'Server-Timing': getHeader()}}) | ||
} | ||
|
||
export const headers = ({actionHeaders}: HeadersArgs) => { | ||
return actionHeaders | ||
} | ||
|
||
const AddMessagePage = () => { | ||
return ( | ||
<div className="rounded-xl bg-white shadow m-4 p-2"> | ||
<h2 className="text-2xl">Add Message</h2> | ||
<form method="POST"> | ||
<div className={fieldsetClasses('grid grid-cols-2')}> | ||
<label className={labelClasses()}> | ||
<span className={labelSpanClasses()}>Title</span> | ||
<input | ||
name="title" | ||
type="text" | ||
className={inputClasses()} | ||
placeholder="A Message" | ||
/> | ||
</label> | ||
<label className={labelClasses()}> | ||
<span className={labelSpanClasses()}>Type</span> | ||
<select name="type" className={inputClasses()}> | ||
<option value="Info">Info</option> | ||
<option value="Warning">Warning</option> | ||
<option value="Danger">Danger</option> | ||
</select> | ||
</label> | ||
<label className={labelClasses('col-span-2')}> | ||
<span className={labelSpanClasses()}>Message</span> | ||
<input | ||
name="message" | ||
type="text" | ||
className={inputClasses()} | ||
placeholder="More <i>details</i> for the message." | ||
/> | ||
</label> | ||
<label className={labelClasses()}> | ||
<span className={labelSpanClasses()}>Start Date</span> | ||
<input name="start-date" type="date" className={inputClasses()} /> | ||
</label> | ||
<label className={labelClasses()}> | ||
<span className={labelSpanClasses()}>End Date</span> | ||
<input name="end-date" type="date" className={inputClasses()} /> | ||
</label> | ||
<label className={labelClasses()}> | ||
<span className={labelSpanClasses()}>Target</span> | ||
<input | ||
name="target" | ||
type="text" | ||
className={inputClasses()} | ||
placeholder="http://target.link/" | ||
/> | ||
</label> | ||
<label className={labelClasses()}> | ||
<span className={labelSpanClasses()}>Scopes</span> | ||
<input | ||
name="scopes" | ||
type="text" | ||
className={inputClasses()} | ||
placeholder="staff, user" | ||
/> | ||
</label> | ||
<button className={buttonClasses('bg-green-300', ['col-start-1'])}> | ||
Add Message | ||
</button> | ||
</div> | ||
</form> | ||
</div> | ||
) | ||
} | ||
|
||
export default AddMessagePage |
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,20 @@ | ||
import {type LoaderArgs, redirect} from '@remix-run/node' | ||
|
||
import {getUPNFromHeaders, getUserFromUPN} from '~/lib/user.server' | ||
import {getPrisma} from '~/lib/prisma' | ||
import {log} from '~/log.server' | ||
|
||
export const loader = async ({request, params}: LoaderArgs) => { | ||
const user = await getUserFromUPN(getUPNFromHeaders(request)) | ||
|
||
if (!user || !user.admin) { | ||
throw new Response('Access Denied', {status: 403}) | ||
} | ||
|
||
const prisma = getPrisma() | ||
|
||
await prisma.infoMessage.delete({where: {id: parseInt(params.id!)}}) | ||
await log('Messages', `Deleted Message #${params.id}`, user.username) | ||
|
||
return redirect('/admin/messages') | ||
} |
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,198 @@ | ||
import { | ||
type ActionArgs, | ||
type LoaderArgs, | ||
json, | ||
redirect, | ||
type HeadersArgs | ||
} from '@remix-run/node' | ||
import {useLoaderData} from '@remix-run/react' | ||
import {invariant} from '@arcath/utils' | ||
import {format} from 'date-fns' | ||
|
||
import {getUserFromUPN, getUPNFromHeaders} from '~/lib/user.server' | ||
import {getPrisma} from '~/lib/prisma' | ||
import {log} from '~/log.server' | ||
import {createTimings} from '~/utils/timings.server' | ||
|
||
import { | ||
labelClasses, | ||
inputClasses, | ||
fieldsetClasses, | ||
labelSpanClasses, | ||
buttonClasses | ||
} from '~/lib/classes' | ||
|
||
export const loader = async ({request, params}: LoaderArgs) => { | ||
const {time, getHeader} = createTimings() | ||
|
||
const user = await time('getUser', 'Get User from header', () => | ||
getUserFromUPN(getUPNFromHeaders(request)) | ||
) | ||
|
||
if (!user || user.type !== 'STAFF') { | ||
throw new Response('Access Denied', {status: 403}) | ||
} | ||
|
||
const prisma = getPrisma() | ||
|
||
const message = await time('getMessage', 'Get the message', () => { | ||
return prisma.infoMessage.findFirstOrThrow({ | ||
where: {id: parseInt(params.id!)} | ||
}) | ||
}) | ||
|
||
return json({user, message}, {headers: {'Server-Timing': getHeader()}}) | ||
} | ||
|
||
export const action = async ({request, params}: ActionArgs) => { | ||
const {time, getHeader} = createTimings() | ||
|
||
const user = await time('getUser', 'Get User from header', () => | ||
getUserFromUPN(getUPNFromHeaders(request)) | ||
) | ||
|
||
if (!user || user.type !== 'STAFF') { | ||
throw new Response('Access Denied', {status: 403}) | ||
} | ||
|
||
const prisma = getPrisma() | ||
|
||
const formData = await request.formData() | ||
|
||
const title = formData.get('title') as string | undefined | ||
const type = formData.get('type') as | ||
| ('Info' | 'Danger' | 'Warning') | ||
| undefined | ||
const message = formData.get('message') as string | undefined | ||
const startDate = formData.get('start-date') as string | undefined | ||
const endDate = formData.get('end-date') as string | undefined | ||
let target = formData.get('target') as string | ||
const scope = formData.get('scopes') as string | undefined | ||
|
||
invariant(title) | ||
invariant(type) | ||
invariant(message) | ||
invariant(startDate) | ||
invariant(endDate) | ||
invariant(scope) | ||
|
||
if (!target) { | ||
target = '#' | ||
} | ||
|
||
const scopes = scope.split(',').map(s => s.trim().toLowerCase()) | ||
|
||
await time('updateMessage', 'Update Message', () => { | ||
return prisma.infoMessage.update({ | ||
where: {id: parseInt(params.id!)}, | ||
data: { | ||
title, | ||
type, | ||
message, | ||
startDate: new Date(startDate), | ||
endDate: new Date(endDate), | ||
target, | ||
scopes | ||
} | ||
}) | ||
}) | ||
|
||
await log('Message', `Updated message ${title}`, user.username) | ||
|
||
return redirect('/admin/messages', {headers: {'Server-Timing': getHeader()}}) | ||
} | ||
|
||
export const headers = ({actionHeaders}: HeadersArgs) => { | ||
return actionHeaders | ||
} | ||
|
||
const AddMessagePage = () => { | ||
const {message} = useLoaderData<typeof loader>() | ||
|
||
return ( | ||
<div className="rounded-xl bg-white shadow m-4 p-2"> | ||
<h2 className="text-2xl">Add Message</h2> | ||
<form method="POST"> | ||
<div className={fieldsetClasses('grid grid-cols-2')}> | ||
<label className={labelClasses()}> | ||
<span className={labelSpanClasses()}>Title</span> | ||
<input | ||
name="title" | ||
type="text" | ||
className={inputClasses()} | ||
placeholder="A Message" | ||
defaultValue={message.title} | ||
/> | ||
</label> | ||
<label className={labelClasses()}> | ||
<span className={labelSpanClasses()}>Type</span> | ||
<select name="type" className={inputClasses()}> | ||
<option value="Info" selected={message.type === 'Info'}> | ||
Info | ||
</option> | ||
<option value="Warning" selected={message.type === 'Warning'}> | ||
Warning | ||
</option> | ||
<option value="Danger" selected={message.type === 'Danger'}> | ||
Danger | ||
</option> | ||
</select> | ||
</label> | ||
<label className={labelClasses('col-span-2')}> | ||
<span className={labelSpanClasses()}>Message</span> | ||
<input | ||
name="message" | ||
type="text" | ||
className={inputClasses()} | ||
placeholder="More <i>details</i> for the message." | ||
defaultValue={message.message} | ||
/> | ||
</label> | ||
<label className={labelClasses()}> | ||
<span className={labelSpanClasses()}>Start Date</span> | ||
<input | ||
name="start-date" | ||
type="date" | ||
className={inputClasses()} | ||
defaultValue={format(new Date(message.startDate), 'yyyy-LL-dd')} | ||
/> | ||
</label> | ||
<label className={labelClasses()}> | ||
<span className={labelSpanClasses()}>End Date</span> | ||
<input | ||
name="end-date" | ||
type="date" | ||
className={inputClasses()} | ||
defaultValue={format(new Date(message.endDate), 'yyyy-LL-dd')} | ||
/> | ||
</label> | ||
<label className={labelClasses()}> | ||
<span className={labelSpanClasses()}>Target</span> | ||
<input | ||
name="target" | ||
type="text" | ||
className={inputClasses()} | ||
placeholder="http://target.link/" | ||
defaultValue={message.target} | ||
/> | ||
</label> | ||
<label className={labelClasses()}> | ||
<span className={labelSpanClasses()}>Scopes</span> | ||
<input | ||
name="scopes" | ||
type="text" | ||
className={inputClasses()} | ||
placeholder="staff, user" | ||
defaultValue={message.scopes.join(', ')} | ||
/> | ||
</label> | ||
<button className={buttonClasses('bg-green-300', ['col-start-1'])}> | ||
Update Message | ||
</button> | ||
</div> | ||
</form> | ||
</div> | ||
) | ||
} | ||
|
||
export default AddMessagePage |
Oops, something went wrong.