Skip to content

Commit

Permalink
Merge pull request #47 from hack4impact-calpoly/33-create-announcemen…
Browse files Browse the repository at this point in the history
…t-schema-and-create-announcement-page

feat: added announcement schema and create announcement page
  • Loading branch information
elhagen13 authored Mar 1, 2025
2 parents de94ed9 + 283481f commit fa5155d
Show file tree
Hide file tree
Showing 6 changed files with 263 additions and 2 deletions.
80 changes: 80 additions & 0 deletions __tests__/CreateAnnouncement.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* @jest-environment jsdom
*/

import React from "react";
import "@testing-library/jest-dom";
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import CreateAnnouncement from "@/app/createAnnouncement/page";

describe("CreateAnnouncement", () => {
it("renders the form correctly", () => {
render(<CreateAnnouncement />);

expect(screen.getByText("New Message")).toBeInTheDocument();
expect(screen.getByLabelText("Send to")).toBeInTheDocument();
expect(screen.getByLabelText("Subject")).toBeInTheDocument();
expect(screen.getByLabelText("Message")).toBeInTheDocument();
expect(screen.getByRole("button", { name: /Send/i })).toBeInTheDocument();
expect(screen.getByRole("button", { name: /Cancel/i })).toBeInTheDocument();
});

it("updates form fields when user types", async () => {
render(<CreateAnnouncement />);

const recipientsInput = screen.getByPlaceholderText("Find recipients") as HTMLInputElement;
const subjectInput = screen.getByPlaceholderText("Subject line here") as HTMLInputElement;
const messageInput = screen.getByPlaceholderText("Type your message here...") as HTMLTextAreaElement;

await userEvent.type(recipientsInput, "testUser");
await userEvent.type(subjectInput, "Meeting Update");
await userEvent.type(messageInput, "Hello everyone, please note the meeting time change.");

expect(recipientsInput.value).toBe("testUser");
expect(subjectInput.value).toBe("Meeting Update");
expect(messageInput.value).toBe("Hello everyone, please note the meeting time change.");
});

it("handles file selection correctly", () => {
render(<CreateAnnouncement />);

const fileInput = screen.getByLabelText(/Add attachment/i);
const file = new File(["sample content"], "sample.txt", { type: "text/plain" });

fireEvent.change(fileInput, { target: { files: [file] } });

// Check if the file name appears correctly
const attachmentText = screen.getByText("sample.txt");
expect(attachmentText).toBeInTheDocument();
});

it("handles form submission correctly", () => {
const logSpy = jest.spyOn(console, "log").mockImplementation();

// Render the component
render(<CreateAnnouncement />);

const recipientsInput = screen.getByPlaceholderText(/Find recipients/i);
const subjectInput = screen.getByPlaceholderText(/Subject line here/i);
const messageTextarea = screen.getByPlaceholderText(/Type your message here/i);

fireEvent.change(recipientsInput, { target: { value: "Test User" } });
fireEvent.change(subjectInput, { target: { value: "Test Subject" } });
fireEvent.change(messageTextarea, { target: { value: "Test Message" } });

// Simulate form submission
const submitButton = screen.getByRole("button", { name: /Send/i });
fireEvent.click(submitButton);

// Check if console.log was called with the correct form data
expect(logSpy).toHaveBeenCalledWith("Submitted Data: ", {
recipients: "Test User",
subject: "Test Subject",
message: "Test Message",
attachment: null,
});

logSpy.mockRestore();
});
});
2 changes: 0 additions & 2 deletions __tests__/TreeEntryForm.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import { render, screen, fireEvent, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { treeIssues } from "@/app/newTreeForm/tree-form-data";
import TreeEntryForm from "@/app/newTreeForm/page";
import { ClerkProvider } from "@clerk/nextjs";
import mockRouter from "next-router-mock";

jest.mock("@clerk/nextjs", () => ({
ClerkProvider: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
Expand Down
11 changes: 11 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
},
"dependencies": {
"@auth0/nextjs-auth0": "^4.0.2",
"@chakra-ui/icons": "^2.2.4",
"@chakra-ui/react": "^2.10.5",
"@clerk/clerk-react": "^5.24.0",
"@clerk/nextjs": "^5.0.9",
Expand Down
153 changes: 153 additions & 0 deletions src/app/createAnnouncement/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
"use client";

import { useState } from "react";
import {
Box,
Button,
FormControl,
FormLabel,
Input,
Textarea,
VStack,
IconButton,
HStack,
useToast,
Flex,
Text,
} from "@chakra-ui/react";
import { AttachmentIcon } from "@chakra-ui/icons";

const CreateAnnouncement = () => {
type AnnouncementData = {
recipients: string;
subject: string;
message: string;
attachment: File | null;
};

const [formData, setFormData] = useState<AnnouncementData>({
recipients: "",
subject: "",
message: "",
attachment: null, // Make sure this is 'attachment'
});

const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {
const { name, value } = e.target;
setFormData((prev) => ({
...prev,
[name]: value,
}));
};

const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.files && e.target.files[0]) {
setFormData((prev) => ({
...prev,
attachment: e.target.files![0],
}));
}
};

const handleSubmit = () => {
console.log("Submitted Data: ", formData);
};

return (
<Box
position="absolute"
width="100vw"
minHeight="100vh"
bg="#F4F1E8"
transform="translateX(-15rem)"
pl="15rem"
display="flex"
alignItems="center"
>
<VStack spacing={7} align="start" p="50px" py="50px" width="100%" maxW="900px" mx="auto">
<Box fontSize="3xl" fontWeight="bold">
New Message
</Box>
<FormControl>
<FormLabel fontWeight="bold">Send to</FormLabel>
<Input
name="recipients"
placeholder="Find recipients"
_placeholder={{ color: "#596435" }}
value={formData.recipients}
onChange={handleChange}
bg="white"
color="#596435"
borderRadius="xl"
py="23px"
/>
</FormControl>
<FormControl>
<FormLabel fontWeight="bold">Subject</FormLabel>
<Input
name="subject"
placeholder="Subject line here"
_placeholder={{ color: "#596435" }}
value={formData.subject}
onChange={handleChange}
bg="white"
borderRadius="xl"
py="23px"
/>
</FormControl>
<FormControl position="relative">
<FormLabel fontWeight="bold">Message</FormLabel>
<Textarea
name="message"
placeholder="Type your message here..."
_placeholder={{ color: "#596435" }}
value={formData.message}
onChange={handleChange}
bg="#white"
h="250px"
pr="40px" // Space for icon
color="#596435"
borderRadius="xl"
/>
<Box position="absolute" top="35px" right="15px" display="flex" alignItems="center">
<label htmlFor="file-upload" style={{ display: "flex", alignItems: "center", cursor: "pointer" }}>
<IconButton
icon={<AttachmentIcon />}
bg="transparent"
color="black"
aria-label="Upload file"
_hover={{ color: "black" }}
as="label"
htmlFor="file-upload"
mr="-6px"
/>
<Text fontSize="sm" color="black">
{formData.attachment ? formData.attachment.name : "Add attachment"}
</Text>
</label>
</Box>
<Input type="file" id="file-upload" display="none" onChange={handleFileChange} />
</FormControl>
<HStack spacing={5}>
<Button px="30px" fontWeight="normal" rounded="full" bg="#596435" color="white" onClick={handleSubmit}>
Send
</Button>
<Button
borderWidth="1.5px"
fontWeight="normal"
rounded="full"
variant="outline"
bg="white"
borderColor="#596435"
color="#596435"
>
Cancel
</Button>
</HStack>
</VStack>
</Box>
// </Box>
);
};

export default CreateAnnouncement;
18 changes: 18 additions & 0 deletions src/database/announcementSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import mongoose, { Schema } from "mongoose";

export type IAnnouncement = {
_id: string;
volunteers: string[];
sender: string;
dateSent: Date;
message: string;
};

const AnnouncementSchema = new Schema<IAnnouncement>({
volunteers: { type: [String], required: true },
sender: { type: String, required: true },
dateSent: { type: Date, required: true },
message: { type: String, required: true },
});

export default mongoose.models.Announcement || mongoose.model<IAnnouncement>("Announcement", AnnouncementSchema);

0 comments on commit fa5155d

Please sign in to comment.