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
11 changes: 11 additions & 0 deletions claude.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,17 @@ Note: `air` runs `task gen-docs` as a pre-command on every rebuild, so `swag` CL
- **Pagination:** Cursor-based with base64-encoded JSON cursors
- **Migrations:** SQL files in `cmd/migrate/migrations/`, managed with `golang-migrate`

#### Migration Naming Convention

Format: `{6-digit-number}_{action}_{subject}.{up|down}.sql`

- `create` — foundational schema objects (infrastructure, core tables, initial types)
- `add` — new features, tables, columns, or triggers added after initial setup
- `alter` — modifications to existing schema objects
- `seed` — initial/default data insertion

Each migration must be isolated to one concern — one table, one type, or one logical operation. Triggers and indexes stay with their parent table. Enum types get their own migration, separate from the table that uses them.

#### Go Handler Pattern

Handlers are methods on `*application`:
Expand Down
14 changes: 2 additions & 12 deletions client/web/src/pages/admin/_shared/grading/GradingDetailsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,8 @@ import { toast } from "sonner";
import { Skeleton } from "@/components/ui/skeleton";
import { fetchApplicationResumeURL } from "@/pages/admin/all-applicants/api";
import {
DemographicsSection,
EducationSection,
EventPreferencesSection,
ExperienceSection,
LinksSection,
PersonalInfoSection,
ShortAnswersSection,
SchemaDetailRenderer,
TimelineSection,
} from "@/pages/admin/all-applicants/components/detail-sections";
import { errorAlert } from "@/shared/lib/api";
Expand Down Expand Up @@ -72,12 +67,7 @@ export const GradingDetailsPanel = memo(function GradingDetailsPanel({

return (
<div className="space-y-8 p-8 pb-10 text-base">
<PersonalInfoSection application={application} />
<DemographicsSection application={application} />
<EducationSection application={application} />
<ExperienceSection application={application} />
<ShortAnswersSection application={application} />
<EventPreferencesSection application={application} />
<SchemaDetailRenderer application={application} />
<LinksSection
application={application}
onViewResume={handleViewResume}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,9 @@ import type { Application } from "@/types";

import { fetchApplicationResumeURL } from "../api";
import { formatName, getStatusColor } from "../utils";
import {
DemographicsSection,
EducationSection,
EventPreferencesSection,
ExperienceSection,
LinksSection,
PersonalInfoSection,
ShortAnswersSection,
TimelineSection,
} from "./detail-sections";
import { LinksSection } from "./detail-sections";
import { SchemaDetailRenderer } from "./detail-sections/SchemaDetailRenderer";
import { TimelineSection } from "./detail-sections/TimelineSection";

interface ApplicationDetailPanelProps {
application: Application | null;
Expand Down Expand Up @@ -75,7 +68,10 @@ export const ApplicationDetailPanel = memo(function ApplicationDetailPanel({
) : application ? (
<>
<p className="font-semibold">
{formatName(application.first_name, application.last_name)}
{formatName(
application.responses?.first_name as string | null,
application.responses?.last_name as string | null,
)}
</p>
<Badge className={getStatusColor(application.status)}>
{application.status}
Expand Down Expand Up @@ -122,12 +118,7 @@ export const ApplicationDetailPanel = memo(function ApplicationDetailPanel({
</div>
) : application ? (
<div className="space-y-6 pb-2">
<PersonalInfoSection application={application} />
<DemographicsSection application={application} />
<EducationSection application={application} />
<ExperienceSection application={application} />
<ShortAnswersSection application={application} />
<EventPreferencesSection application={application} />
<SchemaDetailRenderer application={application} />
<LinksSection
application={application}
onViewResume={handleViewResume}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,14 @@ export const ApplicationsTable = memo(function ApplicationsTable({
</div>
</TableCell>
<TableCell>{app.email}</TableCell>
<TableCell>{app.phone_e164 ?? "-"}</TableCell>
<TableCell>{app.phone ?? "-"}</TableCell>
<TableCell>{app.age ?? "-"}</TableCell>
<TableCell>{app.country_of_residence ?? "-"}</TableCell>
<TableCell>{app.gender ?? "-"}</TableCell>
<TableCell>{app.university ?? "-"}</TableCell>
<TableCell>{app.major ?? "-"}</TableCell>
<TableCell>{app.level_of_study ?? "-"}</TableCell>
<TableCell>{app.hackathons_attended_count ?? "-"}</TableCell>
<TableCell>{app.hackathons_attended ?? "-"}</TableCell>
<TableCell className="whitespace-nowrap">
{app.submitted_at
? new Date(app.submitted_at).toLocaleDateString()
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -15,91 +15,38 @@ export function LinksSection({
onViewResume,
isOpeningResume = false,
}: LinksSectionProps) {
const hasLinks =
application.github ||
application.linkedin ||
application.website ||
application.resume_path;

if (!hasLinks) {
if (!application.resume_path) {
return null;
}

return (
<div>
<h4 className="text-sm font-semibold mb-2">Links</h4>
<h4 className="text-sm font-semibold mb-2">Resume</h4>
<div className="space-y-2 text-sm">
{application.github && (
<div>
<Label className="text-muted-foreground text-xs">GitHub</Label>
<p>
<a
href={application.github}
target="_blank"
rel="noopener noreferrer"
className="text-blue-600 hover:underline break-all cursor-pointer"
>
{application.github}
</a>
</p>
</div>
)}
{application.linkedin && (
<div>
<Label className="text-muted-foreground text-xs">LinkedIn</Label>
<p>
<a
href={application.linkedin}
target="_blank"
rel="noopener noreferrer"
className="text-blue-600 hover:underline break-all cursor-pointer"
>
{application.linkedin}
</a>
</p>
</div>
)}
{application.website && (
<div>
<Label className="text-muted-foreground text-xs">Website</Label>
<p>
<a
href={application.website}
target="_blank"
rel="noopener noreferrer"
className="text-blue-600 hover:underline break-all cursor-pointer"
>
{application.website}
</a>
</p>
</div>
)}
{application.resume_path && (
<div>
<Label className="text-muted-foreground text-xs">Resume</Label>
<div className="pt-1">
<Button
type="button"
variant="outline"
size="sm"
onClick={onViewResume}
disabled={!onViewResume || isOpeningResume}
>
{isOpeningResume ? (
<>
<Loader2 className="h-4 w-4 mr-2 animate-spin" />
Opening...
</>
) : (
<>
<ExternalLink className="h-4 w-4 mr-2" />
View Resume
</>
)}
</Button>
</div>
<div>
<Label className="text-muted-foreground text-xs">Resume</Label>
<div className="pt-1">
<Button
type="button"
variant="outline"
size="sm"
onClick={onViewResume}
disabled={!onViewResume || isOpeningResume}
>
{isOpeningResume ? (
<>
<Loader2 className="h-4 w-4 mr-2 animate-spin" />
Opening...
</>
) : (
<>
<ExternalLink className="h-4 w-4 mr-2" />
View Resume
</>
)}
</Button>
</div>
)}
</div>
</div>
</div>
);
Expand Down
Loading
Loading