Skip to content

Commit d3a8e2d

Browse files
Merge branch 'main' into optimize-roadmap-seo
2 parents 8c30bad + 9fc2157 commit d3a8e2d

18 files changed

Lines changed: 2382 additions & 484 deletions

File tree

CONTRIBUTING.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,17 @@ npm run seed:admin
122122
```
123123

124124
> The unified seed script lives at `server/src/database/seeds/seed.ts`. It is idempotent, you can run it multiple times without creating duplicates. Default login for all seeded users is `Test@1234`.
125+
>
126+
> Seeded accounts:
127+
> | Email | Role |
128+
> |---|---|
129+
> | `admin@internhack.xyz` | Admin |
130+
> | `recruiter@internhack.xyz` | Recruiter |
131+
> | `aarav@example.com` | Student |
132+
> | `priya@example.com` | Student |
133+
> | `rohan@example.com` | Student |
134+
> | `sneha@example.com` | Student |
135+
> | `arjun@example.com` | Student |
125136
126137
### Step 6: Start the dev servers
127138

client/package-lock.json

Lines changed: 30 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/src/module/student/applications/MyApplicationsPage.tsx

Lines changed: 78 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
12
import { Link } from "react-router";
23
import { motion } from "framer-motion";
34
import { Briefcase, MapPin, Building2, ArrowUpRight, Clock, Search, ExternalLink, X } from "lucide-react";
@@ -8,7 +9,7 @@ import { queryKeys } from "../../../lib/query-keys";
89
import type { Application } from "../../../lib/types";
910
import { LoadingScreen } from "../../../components/LoadingScreen";
1011
import { SEO } from "../../../components/SEO";
11-
12+
import toast from "@/components/ui/toast";
1213
interface ExternalApplication {
1314
id: number;
1415
studentId: number;
@@ -214,13 +215,53 @@ const ExternalApplicationCard = React.memo(function ExternalApplicationCard({
214215
});
215216

216217
const PAGE_SIZE = 10;
218+
function WithdrawModal({
219+
open,
220+
onCancel,
221+
onConfirm,
222+
}: {
223+
open: boolean;
224+
onCancel: () => void;
225+
onConfirm: () => void;
226+
}) {
227+
if (!open) return null;
228+
return (
229+
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
230+
<div className="bg-white dark:bg-stone-900 border border-stone-200 dark:border-white/10 rounded-md p-6 max-w-sm w-full mx-4 space-y-4">
231+
<h3 className="text-base font-bold text-stone-900 dark:text-stone-50">
232+
Withdraw Application?
233+
</h3>
234+
<p className="text-sm text-stone-500">
235+
Are you sure you want to withdraw this application? This action cannot be undone.
236+
</p>
237+
<div className="flex gap-3">
238+
<button
239+
onClick={onCancel}
240+
className="flex-1 px-4 py-2 rounded-md text-xs font-mono uppercase tracking-widest text-stone-600 dark:text-stone-400 border border-stone-200 dark:border-white/10 hover:border-stone-400 transition-colors bg-transparent cursor-pointer"
241+
>
242+
Cancel
243+
</button>
244+
<button
245+
onClick={onConfirm}
246+
className="flex-1 px-4 py-2 rounded-md text-xs font-mono uppercase tracking-widest text-white bg-red-500 hover:bg-red-600 transition-colors cursor-pointer border-0"
247+
>
248+
Withdraw
249+
</button>
250+
</div>
251+
</div>
252+
</div>
253+
);
254+
}
255+
256+
217257

218258
export default function MyApplicationsPage() {
219259
const queryClient = useQueryClient();
220260
const [search, setSearch] = useState("");
221261
const [debouncedSearch, setDebouncedSearch] = useState("");
222-
const [page, setPage] = useState(1);
223-
262+
const [page, setPage] = useState(1);
263+
const [withdrawId, setWithdrawId] = useState<number | null>(null);
264+
const [showWithdrawModal, setShowWithdrawModal] = useState(false);
224265
useEffect(() => {
225266
const t = setTimeout(() => setDebouncedSearch(search), 200);
226267
return () => clearTimeout(t);
@@ -263,26 +304,50 @@ export default function MyApplicationsPage() {
263304

264305
const handleWithdraw = useCallback(
265306
async (id: number) => {
266-
if (!confirm("Are you sure you want to withdraw this application?")) return;
267-
try {
268-
await api.delete(`/student/applications/${id}`);
269-
queryClient.setQueryData<Application[]>(queryKeys.applications.mine(), (old) =>
270-
(old ?? []).map((a) => (a.id === id ? { ...a, status: "WITHDRAWN" as const } : a))
271-
);
272-
} catch {
273-
alert("Failed to withdraw");
274-
}
307+
setWithdrawId(id);
308+
setShowWithdrawModal(true);
275309
},
276-
[queryClient]
310+
[]
277311
);
278312

313+
const confirmWithdraw = useCallback(async () => {
314+
if (!withdrawId) return;
315+
const idToWithdraw = withdrawId;
316+
setShowWithdrawModal(false);
317+
setWithdrawId(null);
318+
try {
319+
await api.delete(`/student/applications/${idToWithdraw}`);
320+
queryClient.setQueryData<{
321+
applications: Application[];
322+
externalApplications: ExternalApplication[];
323+
}>(queryKeys.applications.mine(), (old) => {
324+
if (!old) return old;
325+
return {
326+
...old,
327+
applications: old.applications.map((a) =>
328+
a.id === idToWithdraw ? { ...a, status: "WITHDRAWN" as const } : a
329+
),
330+
};
331+
});
332+
toast.success("Application withdrawn successfully");
333+
} catch {
334+
toast.error("Failed to withdraw application");
335+
}
336+
}, [withdrawId, queryClient]);
279337
if (isLoading) return <LoadingScreen />;
280338

339+
340+
281341
const hasSearch = search.trim().length > 0;
282342

283343
return (
284344
<div className="relative pb-16">
285345
<SEO title="My Applications" noIndex />
346+
<WithdrawModal
347+
open={showWithdrawModal}
348+
onCancel={() => setShowWithdrawModal(false)}
349+
onConfirm={confirmWithdraw}
350+
/>
286351

287352
{/* Header */}
288353
<motion.div

client/src/module/student/ats/AtsScorePage.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,17 @@ export default function AtsScorePage() {
374374
resume / ats score
375375
</div>
376376
<h1 className="mt-4 text-4xl sm:text-5xl font-bold tracking-tight text-stone-900 dark:text-stone-50 leading-none">
377-
Score your resume.
377+
Score your{" "}
378+
<span className="relative inline-block">
379+
<span className="relative z-10">resume.</span>
380+
<motion.span
381+
initial={{ scaleX: 0 }}
382+
animate={{ scaleX: 1 }}
383+
transition={{ duration: 0.7, delay: 0.4, ease: "easeOut" }}
384+
aria-hidden
385+
className="absolute bottom-1 left-0 right-0 h-3 md:h-4 bg-lime-400 origin-left z-0"
386+
/>
387+
</span>
378388
</h1>
379389
<p className="mt-3 text-sm text-stone-500 max-w-md">
380390
Upload a PDF, add a target role, and get an ATS score with keyword gaps and concrete rewrite suggestions.

client/src/module/student/job-agent/JobAgentPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ export default function JobAgentPage() {
182182
<SEO title="InternHack AI" noIndex />
183183

184184
{/* Editorial header */}
185-
<div className="shrink-0 border-b border-stone-200 dark:border-white/10 px-4 sm:px-8 pt-6 pb-4 bg-stone-50 dark:bg-stone-950">
185+
<div className="shrink-0 px-4 sm:px-8 pt-6 pb-4 bg-stone-50 dark:bg-stone-950">
186186
<div className="max-w-4xl mx-auto">
187187
<div className="flex items-center gap-2 mb-2">
188188
<div className="h-1 w-1 bg-lime-400"></div>

0 commit comments

Comments
 (0)