Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main' into ticket-317
Browse files Browse the repository at this point in the history
  • Loading branch information
jessherlitz committed Jul 25, 2024
2 parents 69cb85a + cb1adcb commit c732cf8
Show file tree
Hide file tree
Showing 155 changed files with 4,746 additions and 1,338 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,7 @@ yarn-error.log*
# Miscelleanous

.DS_Store

# Other

schema.prisma
19 changes: 19 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ ColorStack members first, and then friends of ColorStack. ❤️
- [Executing Database Migrations](#executing-database-migrations)
- [Seeding the Database](#seeding-the-database)
- [Stopping the Database](#stopping-the-database)
- [Using a Database GUI (Prisma Studio)](#using-a-database-gui-prisma-studio)
- [Building the Project](#building-the-project)
- [Running the Applications](#running-the-applications)
- [Logging Into Applications](#logging-into-applications)
Expand Down Expand Up @@ -156,6 +157,24 @@ recommended to take them down once you are done using them. Run:
yarn dx:down
```

#### Using a Database GUI (Prisma Studio)

To make it easier to interact with and manage your data in the browser, you can
use [Prisma Studio](https://www.prisma.io/studio)!

To get started, setup your Prisma schema file by running:

```
yarn prisma:setup
```

Then, start Prisma Studio locally and open your browser to the URL that gets
printed:

```
yarn prisma:studio
```

### Building the Project

You can build the project by running:
Expand Down
2 changes: 2 additions & 0 deletions CONTRIBUTORS.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,5 @@
- nicholasg2001
- nayoseph
- savazques
- Lumiho
- iperalta7
7 changes: 6 additions & 1 deletion apps/admin-dashboard/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,21 @@ API_URL=http://localhost:8080
DATABASE_URL=postgresql://oyster:oyster@localhost:5433/oyster
ENVIRONMENT=development
JWT_SECRET=_
MEMBER_PROFILE_URL=http://localhost:3000
REDIS_URL=redis://localhost:6380
SESSION_SECRET=_

# Optional for development, but won't be able to run certain features...

# AIRTABLE_API_KEY=
# AIRTABLE_FAMILY_BASE_ID=
# AIRTABLE_MEMBERS_TABLE_ID=
# AIRTABLE_RESUME_BOOKS_BASE_ID=
# GOOGLE_CLIENT_ID=
# GOOGLE_CLIENT_SECRET=
# GOOGLE_DRIVE_RESUME_BOOKS_FOLDER_ID=
# POSTMARK_API_TOKEN=
# SENTRY_DSN=
# SMTP_HOST=
# SMTP_PASSWORD=
# SMTP_USERNAME=
# SMTP_USERNAME=
2 changes: 2 additions & 0 deletions apps/admin-dashboard/app/entry.server.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { createReadableStreamFromReadable } from '@remix-run/node';
import { RemixServer } from '@remix-run/react';
import * as Sentry from '@sentry/remix';
import dayjs from 'dayjs';
import advancedFormat from 'dayjs/plugin/advancedFormat';
import timezone from 'dayjs/plugin/timezone.js';
import utc from 'dayjs/plugin/utc.js';
import isbot from 'isbot';
Expand All @@ -18,6 +19,7 @@ import { ENV } from '@/shared/constants.server';

dayjs.extend(utc);
dayjs.extend(timezone);
dayjs.extend(advancedFormat);

Sentry.init({
dsn: ENV.SENTRY_DSN,
Expand Down
12 changes: 11 additions & 1 deletion apps/admin-dashboard/app/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,17 @@ export const links: LinksFunction = () => {
};

export const meta: MetaFunction = () => {
return [{ title: 'ColorStack | Admin Dashboard' }];
const title = 'Admin Dashboard';

const description =
'Your home for all things ColorStack administration. Manage applications, events and more!';

return [
{ title },
{ name: 'description', content: description },
{ property: 'og:title', content: title },
{ property: 'og:description', content: description },
];
};

export async function loader({ request }: LoaderFunctionArgs) {
Expand Down
2 changes: 1 addition & 1 deletion apps/admin-dashboard/app/routes/_dashboard.admins.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ function AdminsTable() {
},
{
displayName: 'Status',
size: null,
size: '200',
render: (admin) => {
return admin.isArchived ? (
<Pill color="gray-100">Archived</Pill>
Expand Down
14 changes: 10 additions & 4 deletions apps/admin-dashboard/app/routes/_dashboard.events.$id.import.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,14 @@ import { z } from 'zod';

import { db } from '@oyster/db';
import { Email, EventAttendee } from '@oyster/types';
import { Button, Form, getErrors, Modal, validateForm } from '@oyster/ui';
import {
Button,
FileUploader,
Form,
getErrors,
Modal,
validateForm,
} from '@oyster/ui';
import { id } from '@oyster/utils';

import { getEvent, job, parseCsv } from '@/admin-dashboard.server';
Expand Down Expand Up @@ -181,12 +188,11 @@ function ImportEventAttendeesForm() {
return (
<RemixForm className="form" method="post" encType="multipart/form-data">
<Form.Field error={errors.file} labelFor={keys.file} required>
<input
accept=".csv"
<FileUploader
accept={['text/csv']}
id={keys.file}
name={keys.file}
required
type="file"
/>
</Form.Field>

Expand Down
40 changes: 28 additions & 12 deletions apps/admin-dashboard/app/routes/_dashboard.events.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,29 @@ export async function loader({ request }: LoaderFunctionArgs) {
.whereRef('eventAttendees.eventId', '=', 'events.id')
.as('attendees');
},
(eb) => {
return eb
.selectFrom('eventRegistrations')
.select(eb.fn.countAll<string>().as('count'))
.whereRef('eventRegistrations.eventId', '=', 'events.id')
.as('registrations');
},
]);

const events = _events.map((event) => {
return {
...event,
date: dayjs(event.startTime).tz(timezone).format('MM/DD/YY'),
endTime: dayjs(event.endTime).tz(timezone).format('h:mm A'),
startTime: dayjs(event.startTime).tz(timezone).format('h:mm A'),
};
});
const formatter = new Intl.NumberFormat();

const events = _events.map(
({ attendees, endTime, registrations, startTime, ...event }) => {
return {
...event,
attendees: formatter.format(Number(attendees)),
date: dayjs(startTime).tz(timezone).format('MM/DD/YY'),
endTime: dayjs(endTime).tz(timezone).format('h:mm A'),
registrations: formatter.format(Number(registrations)),
startTime: dayjs(startTime).tz(timezone).format('h:mm A'),
};
}
);

return json({
events,
Expand Down Expand Up @@ -143,12 +156,15 @@ function EventsTable() {
size: '400',
render: (event) => event.name,
},
{
displayName: '# of Registrations',
size: '160',
render: (event) => event.registrations,
},
{
displayName: '# of Attendees',
size: '160',
render: (event) => {
return new Intl.NumberFormat().format(Number(event.attendees));
},
render: (event) => event.attendees,
},
{
displayName: 'Type',
Expand All @@ -171,7 +187,7 @@ function EventsTable() {
{
displayName: 'Time',
render: (event) => `${event.startTime} - ${event.endTime}`,
size: null,
size: '200',
},
];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ function FeatureFlagsTable() {
},
{
displayName: 'Description',
size: null,
size: '800',
render: (flag) => flag.description,
},
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ function OnboardingSessionsTable() {
{
displayName: 'Attendees',
render: (session) => session.attendees,
size: null,
size: '800',
},
{
displayName: 'Uploaded By',
Expand Down
116 changes: 116 additions & 0 deletions apps/admin-dashboard/app/routes/_dashboard.resume-books.$id.edit.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import {
type ActionFunctionArgs,
json,
type LoaderFunctionArgs,
redirect,
} from '@remix-run/node';
import {
Form as RemixForm,
useActionData,
useLoaderData,
} from '@remix-run/react';
import dayjs from 'dayjs';

import { getResumeBook, updateResumeBook } from '@oyster/core/resume-books';
import {
RESUME_BOOK_TIMEZONE,
UpdateResumeBookInput,
} from '@oyster/core/resume-books.types';
import {
ResumeBookEndDateField,
ResumeBookHiddenField,
ResumeBookNameField,
ResumeBookStartDateField,
} from '@oyster/core/resume-books.ui';
import { Button, getErrors, Modal, validateForm } from '@oyster/ui';

import { Route } from '@/shared/constants';
import {
commitSession,
ensureUserAuthenticated,
toast,
} from '@/shared/session.server';

export async function loader({ params, request }: LoaderFunctionArgs) {
await ensureUserAuthenticated(request);

const resumeBook = await getResumeBook({
select: ['endDate', 'hidden', 'name', 'startDate'],
where: { id: params.id as string },
});

if (!resumeBook) {
throw new Response(null, { status: 404 });
}

// We need to format the dates so that they respect the <input type="date" />
// format.
const format = 'YYYY-MM-DD';
const tz = RESUME_BOOK_TIMEZONE;

return json({
endDate: dayjs(resumeBook.endDate).tz(tz).format(format),
hidden: resumeBook.hidden,
name: resumeBook.name,
startDate: dayjs(resumeBook.startDate).tz(tz).format(format),
});
}

export async function action({ params, request }: ActionFunctionArgs) {
const session = await ensureUserAuthenticated(request);

const { data, errors, ok } = await validateForm(
request,
UpdateResumeBookInput.omit({ id: true })
);

if (!ok) {
return json({ errors }, { status: 400 });
}

await updateResumeBook({
endDate: data.endDate,
hidden: data.hidden,
id: params.id as string,
name: data.name,
startDate: data.startDate,
});

toast(session, {
message: 'Updated resume book.',
type: 'success',
});

return redirect(Route['/resume-books'], {
headers: {
'Set-Cookie': await commitSession(session),
},
});
}

export default function EditResumeBookModal() {
const { endDate, hidden, name, startDate } = useLoaderData<typeof loader>();
const { errors } = getErrors(useActionData<typeof action>());

return (
<Modal onCloseTo={Route['/resume-books']}>
<Modal.Header>
<Modal.Title>Edit Resume Book</Modal.Title>
<Modal.CloseButton />
</Modal.Header>

<RemixForm className="form" method="post">
<ResumeBookNameField defaultValue={name} error={errors.name} />
<ResumeBookStartDateField
defaultValue={startDate}
error={errors.startDate}
/>
<ResumeBookEndDateField defaultValue={endDate} error={errors.endDate} />
<ResumeBookHiddenField defaultValue={hidden} error={errors.hidden} />
<Button.Group>
<Button.Submit>Edit</Button.Submit>
</Button.Group>
</RemixForm>
</Modal>
);
}
Loading

0 comments on commit c732cf8

Please sign in to comment.