Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/admin-dashboard/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ STUDENT_PROFILE_URL=http://localhost:3000
# SMTP_HOST=
# SMTP_PASSWORD=
# SMTP_USERNAME=
# APIFY_API_TOKEN=
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import {
type ActionFunctionArgs,
data,
Form,
type LoaderFunctionArgs,
redirect,
useActionData,
useLoaderData,
} from 'react-router';

import { deleteCompany, getCompany } from '@oyster/core/employment/server';
import { Button, ErrorMessage, getErrors, Modal } 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 company = await getCompany({
select: ['id', 'name'],
where: { id: params.id as string },
});

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

return {
company,
};
}

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

const result = await deleteCompany(params.id as string);

if (!result.ok) {
return data({ error: result.error }, { status: result.code });
}

toast(session, {
message: 'Deleted company.',
type: 'success',
});

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

export default function DeleteCompanyModal() {
const { company } = useLoaderData<typeof loader>();
const { error } = getErrors(useActionData<typeof action>());

return (
<Modal onCloseTo={Route['/companies']}>
<Modal.Header>
<Modal.Title>Delete Company: {company.name}</Modal.Title>
<Modal.CloseButton />
</Modal.Header>

<Modal.Description>
Are you sure you want to delete this company? This will remove it from
all resume book sponsorships. Companies with work experiences, job
offers, or member sponsor selections cannot be deleted.
</Modal.Description>

<Form className="form" method="post">
<ErrorMessage>{error}</ErrorMessage>

<Button.Group>
<Button.Submit color="error">Delete</Button.Submit>
</Button.Group>
</Form>
</Modal>
);
}
175 changes: 175 additions & 0 deletions apps/admin-dashboard/app/routes/_dashboard.companies.$id.edit.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import {
type ActionFunctionArgs,
data,
Form,
type LoaderFunctionArgs,
redirect,
useActionData,
useLoaderData,
} from 'react-router';

import {
getCompany,
isApifyConfigured,
updateCompany,
} from '@oyster/core/employment/server';
import { AdminUpdateCompanyInput } from '@oyster/core/employment/types';
import {
CompanyDomainField,
CompanyImageUrlField,
CompanyLinkedInSlugField,
CompanyLogoPreview,
CompanyNameField,
CompanyResumeBooksField,
} from '@oyster/core/employment/ui';
import {
listResumeBooks,
updateCompanyResumeBookSponsorships,
} from '@oyster/core/resume-books';
import { db } from '@oyster/db';
import {
Button,
ErrorMessage,
getErrors,
Modal,
Text,
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 companyId = params.id as string;

const [company, resumeBooks, sponsorships] = await Promise.all([
getCompany({
select: ['domain', 'id', 'imageUrl', 'linkedinSlug', 'name'],
where: { id: companyId },
}),

listResumeBooks({
select: ['id', 'name'],
}),

db
.selectFrom('resumeBookSponsors')
.select(['resumeBookId'])
.where('companyId', '=', companyId)
.execute(),
]);

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

return {
apifyConfigured: isApifyConfigured(),
company,
resumeBookIds: sponsorships.map((sponsorship) => sponsorship.resumeBookId),
resumeBooks,
};
}

export async function action({ params, request }: ActionFunctionArgs) {
const session = await ensureUserAuthenticated(request);
const companyId = params.id as string;

const result = await validateForm(request, AdminUpdateCompanyInput);

if (!result.ok) {
return data(result, { status: 400 });
}

const { resumeBookIds, ...companyInput } = result.data;

const updateResult = await updateCompany({
...companyInput,
id: companyId,
});

if (!updateResult.ok) {
return data({ error: updateResult.error }, { status: updateResult.code });
}

const sponsorshipResult = await updateCompanyResumeBookSponsorships({
companyId,
resumeBookIds,
});

if (!sponsorshipResult.ok) {
return data(
{ error: sponsorshipResult.error },
{ status: sponsorshipResult.code }
);
}

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

const url = new URL(request.url);

url.pathname = Route['/companies'];

return redirect(url.toString(), {
headers: {
'Set-Cookie': await commitSession(session),
},
});
}

export default function EditCompanyPage() {
const { apifyConfigured, company, resumeBookIds, resumeBooks } =
useLoaderData<typeof loader>();
const { error, errors } = getErrors(useActionData<typeof action>());

return (
<Modal onCloseTo={Route['/companies']}>
<Modal.Header>
<Modal.Title>Edit Company: {company.name}</Modal.Title>
<Modal.CloseButton />
</Modal.Header>

<Form className="form" method="post">
<CompanyLogoPreview imageUrl={company.imageUrl} />
<CompanyNameField defaultValue={company.name} error={errors.name} />
<CompanyDomainField
defaultValue={company.domain || ''}
error={errors.domain}
/>
<CompanyImageUrlField
defaultValue={company.imageUrl || ''}
error={errors.imageUrl}
/>
<CompanyLinkedInSlugField
defaultValue={company.linkedinSlug || ''}
error={errors.linkedinSlug}
/>
{!apifyConfigured && (
<Text color="gray-500" variant="sm">
Apify is not configured. Use Logo URL to set the logo manually.
</Text>
)}
<CompanyResumeBooksField
defaultValue={resumeBookIds}
error={errors.resumeBookIds || error}
resumeBooks={resumeBooks}
/>

<ErrorMessage>{error}</ErrorMessage>

<Button.Group>
<Button.Submit>Save Changes</Button.Submit>
</Button.Group>
</Form>
</Modal>
);
}
Loading
Loading