Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
6aff3de
fixed listing type
wderocco8 May 10, 2025
8ba7945
fix: proper role assignments in session
jinyoungbang May 11, 2025
825847b
chore: remove logs
jinyoungbang May 11, 2025
f79437a
moved dateCreated to backend
wderocco8 May 20, 2025
baa3835
Merge pull request #186 from whyphi/fix/roles-session]
jinyoungbang May 20, 2025
53ef673
renamed endpoint for applications
wderocco8 May 22, 2025
95c4c1a
Merge branch 'dev/v3.0' into refactor/will/migrate-supabase
wderocco8 May 22, 2025
889c6d0
fixed naming for context in settings
wderocco8 May 22, 2025
29c03be
fixed id usage and prefilled gradYear (to ensure valid integer)
wderocco8 May 24, 2025
2f8e595
removed events from Form.tsx and formatted file
wderocco8 May 24, 2025
dcfbd14
snake case for Listing type
wderocco8 May 26, 2025
e9cccfd
conslidated and cleaned listing type
wderocco8 May 27, 2025
50f8c49
added type and switched to snake_case (for backend)
wderocco8 May 27, 2025
8f5fa19
improved data initalization
wderocco8 May 27, 2025
6feeb08
Merge pull request #188 from whyphi/feat/will/migration/case-converter
wderocco8 May 27, 2025
6a66091
fixed types for applicant and resolved deps
wderocco8 May 28, 2025
ce730a0
made responses nullable
wderocco8 May 28, 2025
c5b8c2c
removed includeEventsAttended (not needed) and added data normalization
wderocco8 May 28, 2025
ad5dcd9
null check for applicant.responses
wderocco8 May 28, 2025
a02a8e0
Merge pull request #189 from whyphi/fix/will/migration/applicant-types
wderocco8 May 28, 2025
238c0d4
fixed spelling and grammar
wderocco8 Jun 7, 2025
56196ed
Merge pull request #190 from whyphi/refactor/will/migrate-supabase
wderocco8 Jun 7, 2025
227c547
Merge pull request #191 from whyphi/dev/v3.0
wderocco8 Jun 7, 2025
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
221 changes: 92 additions & 129 deletions app/admin/create/page.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,22 @@
"use client"
"use client";
import React, { useState } from "react";
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import { useRouter } from 'next/navigation'
import { Checkbox, Label, TextInput } from 'flowbite-react';
import { useRouter } from "next/navigation";
import { Checkbox, Label, TextInput } from "flowbite-react";
import { useAuth } from "@/app/contexts/AuthContext";
import { AdminTextStyles } from "@/styles/TextStyles";



interface FormData {
title: string;
questions: [] | { question: string, context: string }[];
questions: [] | { question: string; context: string }[];
deadline: Date;
includeEventsAttended: boolean;
}

const initialValues: FormData = {
title: "",
questions: [] as { question: string; context: string }[], // Specify the type here
deadline: new Date(),
includeEventsAttended: false
};

export default function Create() {
Expand All @@ -29,31 +25,30 @@ export default function Create() {
const [formData, setFormData] = useState<FormData>(initialValues);
const [selectedDate, setSelectedDate] = useState(new Date());


const handleSubmit = async () => {
const currentDate = new Date();
const formattedDeadline = selectedDate.toISOString();
const formDataWithDates = {
...formData,
dateCreated: currentDate.toISOString(),
deadline: formattedDeadline,
};

try {
const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/create`, {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify(formDataWithDates),
});
const response = await fetch(
`${process.env.NEXT_PUBLIC_API_BASE_URL}/create`,
{
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify(formDataWithDates),
}
);

if (response.ok) {
// The request was successful, you can handle the response here if needed.
console.log("Request successful!");
router.push("/admin"); // Replace "/admin" with the actual route to your admin page

} else {
// Handle the case where the request was not successful.
console.error("Request failed with status:", response.status);
Expand All @@ -64,17 +59,16 @@ export default function Create() {
}
};

function flattenQuestions(questions: { [key: string]: string }[]): { [key: string]: string } {
function flattenQuestions(questions: { [key: string]: string }[]): {
[key: string]: string;
} {
// Flatten the 'questions' array into a flat object
const flattenedQuestions = questions.reduce(
(acc, question, index) => {
Object.keys(question).forEach((key) => {
acc[`questions[${index}].${key}`] = question[key];
});
return acc;
},
{} as Record<string, string>
);
const flattenedQuestions = questions.reduce((acc, question, index) => {
Object.keys(question).forEach((key) => {
acc[`questions[${index}].${key}`] = question[key];
});
return acc;
}, {} as Record<string, string>);

return flattenedQuestions;
}
Expand All @@ -89,21 +83,20 @@ export default function Create() {
};

// Encode the form data into a query parameter string
const { questions, includeEventsAttended, ...formDataStringsOnly } = formDataWithDates;
const flattenedQuestions = flattenQuestions(formData.questions)
const includeEventsAttendedString = includeEventsAttended.toString()

const { questions, ...formDataStringsOnly } = formDataWithDates;
const flattenedQuestions = flattenQuestions(formData.questions);

const formDataQueryString = new URLSearchParams({
...formDataStringsOnly,
...flattenedQuestions,
'includeEventsAttended': includeEventsAttendedString
});

// Open a new tab and navigate to the preview route with form data as query parameters
window.open(`/admin/create/preview/listing?${formDataQueryString}`, '_blank');

}
window.open(
`/admin/create/preview/listing?${formDataQueryString}`,
"_blank"
);
};

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { id, value } = e.target;
Expand All @@ -113,14 +106,6 @@ export default function Create() {
}));
};

const handleCheckboxChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { id, checked } = e.target;
setFormData((prevData) => ({
...prevData,
[id]: checked,
}));
};

const handleAddQuestion = () => {
setFormData((prevData) => ({
...prevData,
Expand All @@ -137,7 +122,11 @@ export default function Create() {
}));
};

const handleQuestionChange = (index: number, field: string, value: string) => {
const handleQuestionChange = (
index: number,
field: string,
value: string
) => {
const updatedQuestions = [...formData.questions];
const questionObj = updatedQuestions[index];

Expand All @@ -152,8 +141,6 @@ export default function Create() {
}
};



const renderInput = (
id: keyof FormData,
label: string,
Expand All @@ -178,63 +165,63 @@ export default function Create() {
const renderQuestions = () => {
return (
<div>
<div className={`flex flex-col border rounded p-4 ${AdminTextStyles.default}`}>
{formData.questions.length === 0 ? (
"None"
) : (
formData.questions.map((questionObj, index) => (
<div className="w-full" key={index}>
<div className="flex justify-end">
<svg
onClick={() => handleRemoveQuestion(index)}
className="w-4 h-4 text-gray-800 dark:text-white hover:bg-gray-100"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 20 20"
style={{ cursor: 'pointer' }}
>
<path
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="m13 7-6 6m0-6 6 6m6-3a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
<div
className={`flex flex-col border rounded p-4 ${AdminTextStyles.default}`}
>
{formData.questions.length === 0
? "None"
: formData.questions.map((questionObj, index) => (
<div className="w-full" key={index}>
<div className="flex justify-end">
<svg
onClick={() => handleRemoveQuestion(index)}
className="w-4 h-4 text-gray-800 dark:text-white hover:bg-gray-100"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 20 20"
style={{ cursor: "pointer" }}
>
<path
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="m13 7-6 6m0-6 6 6m6-3a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
/>
</svg>
</div>
<div className="mb-6">
<label className={`block ${AdminTextStyles.subtext}`}>
Question <span className="text-red-500">*</span>
</label>
<TextInput
id={`question-${index}`} // Set a unique id for each question input
type="text"
placeholder="Question"
value={questionObj.question}
onChange={(e) =>
handleQuestionChange(index, "question", e.target.value)
}
/>
</svg>
</div>
<div className="mb-6">
<label className={`block ${AdminTextStyles.subtext}`}>
Question <span className="text-red-500">*</span>
</label>
<TextInput
id={`question-${index}`} // Set a unique id for each question input
type="text"
placeholder="Question"
value={questionObj.question}
onChange={(e) =>
handleQuestionChange(index, "question", e.target.value)
}
/>
</div>
<div className="mb-6">
<label className={`block ${AdminTextStyles.subtext}`}>
Additional Context / Subheadings
</label>
<TextInput
id={`additional-${index}`} // Set a unique id for each additional input
type="text"
placeholder="Add any additional text that explains the question here"
value={questionObj.context}
onChange={(e) =>
handleQuestionChange(index, "context", e.target.value)
}
/>
</div>
<div className="mb-6">
<label className={`block ${AdminTextStyles.subtext}`}>
Additional Context / Subheadings
</label>
<TextInput
id={`additional-${index}`} // Set a unique id for each additional input
type="text"
placeholder="Add any additional text that explains the question here"
value={questionObj.context}
onChange={(e) =>
handleQuestionChange(index, "context", e.target.value)
}
/>
</div>
<hr className="h-px mb-2 bg-gray-200 border-0 dark:bg-gray-700" />
</div>
<hr className="h-px mb-2 bg-gray-200 border-0 dark:bg-gray-700" />
</div>
))
)}
))}
</div>
<button
onClick={handleAddQuestion}
Expand All @@ -259,8 +246,6 @@ export default function Create() {
Add Question
</button>
</div>


);
};

Expand Down Expand Up @@ -293,37 +278,16 @@ export default function Create() {
);
};

const renderAdditionalSection = () => {
return (
<div className="w-full mb-6">
<label className={`block ${AdminTextStyles.default}`}>
Additional
</label>
<div className="flex items-center gap-2">
<Checkbox
checked={formData.includeEventsAttended}
onChange={handleCheckboxChange}
id="includeEventsAttended"
className="focus:ring-purple-500 dark:focus:ring-purple-600 text-purple-600" />
<Label htmlFor="includeEventsAttended" className="font-light">I want to collect <span className="font-medium underline">Events Attended</span> data</Label>
</div>
</div>
)
}

return (
<form onSubmit={handleSubmit} className="flex flex-col mb-8">
<h1 className={AdminTextStyles.title}>Create a New Listing</h1>

{renderInput("title", "Title", "text", true)}

<label className={AdminTextStyles.default}>
Questions
</label>
<label className={AdminTextStyles.default}>Questions</label>

{renderQuestions()}
{renderDeadline()}
{renderAdditionalSection()}

<div className="flex gap-4">
<button
Expand All @@ -342,7 +306,6 @@ export default function Create() {
Preview
</button>
</div>

</form>
);
}
4 changes: 2 additions & 2 deletions app/admin/listing/[listingId]/insights/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ export default function Insights({ params }: { params: { listingId: string } })
}

const handleRowClick = (applicant: Applicant) => {
localStorage.setItem(selectedApplicantIdKey, applicant.applicantId);
localStorage.setItem(selectedApplicantIdKey, applicant.id);
router.push(`/admin/listing/${params.listingId}`);
};

Expand All @@ -145,7 +145,7 @@ export default function Insights({ params }: { params: { listingId: string } })
onClick={() => handleRowClick(applicant)}
>
<Table.Cell className="whitespace-nowrap font-medium text-gray-900 dark:text-white">
{applicant.firstName} {applicant.lastName}
{applicant.first_name} {applicant.last_name}
</Table.Cell>
{renderCell(applicant)}
</Table.Row>
Expand Down
10 changes: 5 additions & 5 deletions app/admin/listing/[listingId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export default function Listing({ params }: { params: { listingId: string } }) {
// check that applicant exists in localStorage
if (selectedApplicantId) {
// Find the index of the applicant in the applicantData array
const applicantIndex = data.findIndex(applicant => selectedApplicantId == applicant.applicantId);
const applicantIndex = data.findIndex(applicant => selectedApplicantId == applicant.id);

// Check if an applicant was found
if (applicantIndex !== -1) {
Expand Down Expand Up @@ -116,13 +116,13 @@ export default function Listing({ params }: { params: { listingId: string } }) {
setSelectedApplicantIndex(index);

// push to localStorage
localStorage.setItem(selectedApplicantIdKey, applicant.applicantId);
localStorage.setItem(selectedApplicantIdKey, applicant.id);
}}
>
<Table.Cell className="whitespace-nowrap font-medium text-gray-900 dark:text-white">
{applicant.firstName} {applicant.lastName}
{applicant.first_name} {applicant.last_name}
</Table.Cell>
<Table.Cell>{applicant.gradYear}</Table.Cell>
<Table.Cell>{applicant.grad_year}</Table.Cell>
<Table.Cell>
{/* Filter and render only true values */}
{Object.entries(applicant.colleges)
Expand Down Expand Up @@ -177,7 +177,7 @@ export default function Listing({ params }: { params: { listingId: string } }) {
setSelectedApplicantIndex(index);

// push to localStorage
localStorage.setItem(selectedApplicantIdKey, applicant.applicantId);
localStorage.setItem(selectedApplicantIdKey, applicant.id);
};

const scrollApplicantIntoView = (index: number) => {
Expand Down
Loading