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
71 changes: 54 additions & 17 deletions client/src/components/ui/backend/organization_call_backend.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @author sylee212
// src/hooks/organization_call_backend.ts
// Change this URL to match your backend API endpoint

Expand All @@ -7,14 +8,23 @@ import { generateRandomMockInventoryDetails } from "@/mocks/Inventory_Details_In
import { generateMockMember } from "@/mocks/Members_Details_Interface_Mocks";

// tha main URL
export const BASE_URL = "http://localhost:8000/api/activities/";
export const BASE_INVENTORY_URL = "http://localhost:8000/api/inventory/";
export const WEEKLY_REPORT_BY_FIELD =
"http://localhost:8000/api/inventory/weeklyReportByField/";

export interface django_count_response_interface {
field: number;
thisWeek: number;
lastWeek: number;
difference: number;
}

// change when backend is ready
const isDev: boolean = true;
const isDev: boolean = false;

// --- GET: Fetch all items ---
export const getItems = async (
filterType: string,
URL: string,
): Promise<Inventory_Details_Interface[]> => {
if (isDev) {
// Mock data for development
Expand All @@ -27,7 +37,8 @@ export const getItems = async (
} else {
// 1. Fetch from Django
// fetch() is used to get the data from backend using URL
const response = await fetch(`${BASE_URL}?type=${filterType}`);
// `${}` is place holders for code, anything inside the curly braces is code
const response = await fetch(`${URL}`);

// 2. Check if the request was successful
if (!response.ok) throw new Error("Failed to fetch");
Expand All @@ -41,6 +52,22 @@ export const getItems = async (
}
};

export const getWeeklyReportByField = async (
URL: string,
): Promise<django_count_response_interface> => {
const response = await fetch(`${URL}`);

// 2. Check if the request was successful
if (!response.ok) throw new Error("Failed to fetch");

// 3. Parse the JSON data
// because fetcah returns a response, it isnt the data yet,
// its just the response header,
// you will need to use .json() to get the data
// it converts raw bytes to Javascript objects
return await response.json(); // Returns the list from Django
};

// --- POST: Create a new item ---
// .stringify, flattens the object
// headers: { 'Content-Type': 'application/json' } determine, how the data will be dealt with by the server
Expand All @@ -53,34 +80,43 @@ export const createItem = async (
console.log("Mock create item called with data:", newData);
return true; // Simulate successful creation
} else {
const response = await fetch(BASE_URL, {
const response = await fetch(BASE_INVENTORY_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(newData),
});

// must return true when coding backend
return await response.json();
if (response.ok) return true;

// If it's not OK, let's see why
const errorText = await response.text();
console.error("Server Error Response:", errorText);
return false;
}
};

// --- PATCH/PUT: Update an existing item ---
export const updateItem = async (
id: number,
newData: Inventory_Details_Interface,
) => {
// Django usually expects a trailing slash after the ID
const response = await fetch(`${BASE_URL}${id}/`, {
method: "PATCH",
): Promise<boolean> => {
const response = await fetch(`${BASE_INVENTORY_URL}${id}/`, {
method: "PATCH", // PATCH is better than PUT for Partial updates
headers: { "Content-Type": "application/json" },
body: JSON.stringify(newData),
});
return await response.json();

if (response.ok) return true;

// If it's not OK, let's see why
const errorText = await response.text();
console.error("Server Error Response:", errorText);
return false;
};

// --- DELETE: Remove an item ---
export const deleteItem = async (id: number) => {
const response = await fetch(`${BASE_URL}${id}/`, {
const response = await fetch(`${BASE_INVENTORY_URL}${id}/`, {
method: "DELETE",
});
// DELETE usually returns a 204 No Content status, so we don't always .json() it
Expand All @@ -89,6 +125,7 @@ export const deleteItem = async (id: number) => {

// MEMBERS SECTION

// PENDING
export const getMembers = async (
filterType: string,
): Promise<Member_Details_Interface[]> => {
Expand All @@ -103,7 +140,7 @@ export const getMembers = async (
} else {
// 1. Fetch from Django
// fetch() is used to get the data from backend using URL
const response = await fetch(`${BASE_URL}?type=${filterType}`);
const response = await fetch(`${BASE_INVENTORY_URL}?type=${filterType}`);

// 2. Check if the request was successful
if (!response.ok) throw new Error("Failed to fetch");
Expand All @@ -124,7 +161,7 @@ export const createMember = async (
console.log("Mock create Member called with data:", newData);
return true; // Simulate successful creation
} else {
const response = await fetch(BASE_URL, {
const response = await fetch(BASE_INVENTORY_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(newData),
Expand All @@ -141,7 +178,7 @@ export const updateMember = async (
newData: Member_Details_Interface,
) => {
// Django usually expects a trailing slash after the ID
const response = await fetch(`${BASE_URL}${id}/`, {
const response = await fetch(`${BASE_INVENTORY_URL}${id}/`, {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(newData),
Expand All @@ -151,7 +188,7 @@ export const updateMember = async (

// --- DELETE: Remove an Member ---
export const deleteMember = async (id: number) => {
const response = await fetch(`${BASE_URL}${id}/`, {
const response = await fetch(`${BASE_INVENTORY_URL}${id}/`, {
method: "DELETE",
});
// DELETE usually returns a 204 No Content status, so we don't always .json() it
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
// @author sylee212
// src/hooks/organization_clean_backend_calls.ts
import SWR, { KeyedMutator } from "swr";
import useSWR, { KeyedMutator } from "swr";

import { Member_Details_Interface } from "@/components/ui/card_organization_member_details_modal";

import type { Inventory_Details_Interface } from "../card_organization_inventory_details_modal";
import {
BASE_URL,
BASE_INVENTORY_URL,
createItem,
createMember,
deleteItem,
django_count_response_interface,
getItems,
getMembers,
getWeeklyReportByField,
updateItem,
updateMember,
} from "./organization_call_backend";
Expand Down Expand Up @@ -45,6 +48,17 @@ export interface organization_clean_backend_calls_return_boolean_members_interfa
refresh: KeyedMutator<boolean>; // Optional refresh function
}

export interface organization_clean_backend_calls_return_count_interface {
// data? means "if data exists". ?? 0 means "otherwise use 0"
thisWeek: number;
lastWeek: number;
difference: number;
loading: boolean;
error: Error | null;
isSuccess: boolean;
refresh: KeyedMutator<django_count_response_interface>; // Optional refresh function
}

/*
This is the class that will call the backend to get the item data / set the item data / update the item data/ delete the item data

Expand All @@ -54,17 +68,15 @@ Remember, this will only be invoked once, when its mounted, if you want to call
// 1. Define a simple fetcher function (standard for SWR)
// const fetcher = (url: string) => fetch(url).then(res => res.json());

// this is the one that will work with the clean function from api call
const fetcher = () => getItems("all");

/*
Even if we are calling mocks, we need to SWR

filterType: d to add to backend url call
isDev: true if we want to mock data
*/
export const useOrganizationBackendGetItems = (
filterType: string,
category: string,
sortBy: string,
): organization_clean_backend_calls_return_interface => {
// 2. SWR handles the state, the effect, and the async logic
// SWR(key, fetcher, options)
Expand Down Expand Up @@ -116,9 +128,27 @@ export const useOrganizationBackendGetItems = (
// mutate? what is that, so lets say we do polling every 30seconds and
// ther is an inventory update that happened in between the 30seconds
// SWR will immediately
const { data, error, isLoading, mutate } = SWR(
[`${BASE_URL}`, filterType], // The "Key" (Unique identifier)
fetcher, // The "Fetcher" (Your function)

// generate the URL
// 1. GENERATE THE URL STRING
// Instead of complex if/else, we use URLSearchParams
const params = new URLSearchParams();
if (category && category !== "") params.append("categories", category); // Django expects 'categories'
if (sortBy && sortBy !== "") params.append("ordering", sortBy); // Django expects 'ordering'

// If params exist, add '?' and the params, otherwise just the base URL
const queryString = params.toString();

// 2. Fix the URL construction
// We need backticks ` ` to use ${}
// We need to add 'items/' because the router is registered there
const finalUrl = queryString
? `${BASE_INVENTORY_URL}?${queryString}`
: `${BASE_INVENTORY_URL}`;

const { data, error, isLoading, mutate } = useSWR(
finalUrl, // The "Key" (Unique identifier)
getItems, // The "Fetcher" (Your function)
{
refreshInterval: 30, // Poll every 30 seconds 30000ms
revalidateOnFocus: true, // Refresh when r clicks back into the tab
Expand All @@ -136,6 +166,32 @@ export const useOrganizationBackendGetItems = (
return res;
};

export const useOrganizationBackendGetWeeklyReportByField = (
URL: string,
): organization_clean_backend_calls_return_count_interface => {
const { data, error, isLoading, mutate } = useSWR(
URL, // The "Key" (Unique identifier)
getWeeklyReportByField, // The "Fetcher" (Your function)
{
refreshInterval: 30, // Poll every 30 seconds 30000ms
revalidateOnFocus: true, // Refresh when r clicks back into the tab
},
);

const res: organization_clean_backend_calls_return_count_interface = {
thisWeek: data?.thisWeek || 0,
lastWeek: data?.lastWeek || 0,
difference: data?.difference || 0,

loading: isLoading,
error: error,
isSuccess: !isLoading && !error,
refresh: mutate, // SWR calls its refresh function "mutate"
};

return res;
};

// This is now a standard function, NOT a hook
// if it is a hook, it will not work, a hook means swr
export const createItemClean = async (
Expand Down Expand Up @@ -180,12 +236,13 @@ Even if we are calling mocks, we need to SWR

filterType: d to add to backend url call
isDev: true if we want to mock data
// PENDING
*/
export const useOrganizationBackendGetMembers = (
filterType: string,
): organization_clean_backend_calls_return_get_members_interface => {
const { data, error, isLoading, mutate } = SWR(
[`${BASE_URL}`, filterType], // The "Key" (Unique identifier)
const { data, error, isLoading, mutate } = useSWR(
[`${BASE_INVENTORY_URL}`, filterType], // The "Key" (Unique identifier)
fetcher2, // The "Fetcher" (Your function)
{
refreshInterval: 30, // Poll every 30 seconds 30000ms
Expand Down Expand Up @@ -246,7 +303,7 @@ export const deleteMemberClean = async (memberId: number): Promise<boolean> => {

// // to SWR for PUT
// const { data, error, isLoading, mutate } = SWR(
// [`${BASE_URL}`, "newItem"],
// [`${BASE_INVENTORY_URL}`, "newItem"],
// () => createItem(itemData),
// {
// revalidateOnFocus: true,
Expand Down
1 change: 1 addition & 0 deletions client/src/components/ui/button_quick_actions.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @author sylee212
// src/components/button_quick_actions.tsx

import Link from "next/link";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @author sylee212
// src/components/card_organization_inventory_details_modal.tsx

/*
Expand Down Expand Up @@ -27,7 +28,7 @@ interface Inventory_Details_Interface {
name: string;
details: string;
categories?: string;
availability?: string;
availability?: boolean;
organization?: string;
collectionPoint: string;
borrowerName?: string;
Expand All @@ -45,6 +46,11 @@ interface Inventory_Details_Modal_Interface {
itemData: Inventory_Details_Interface | null; // Data of the item to display
}

const deleteHelper = (id: number, onclose: () => void) => {
deleteItemClean(id || 0);
onclose();
};

const Inventory_Details_Modal: React.FC<Inventory_Details_Modal_Interface> = ({
isOpen,
onClose,
Expand Down Expand Up @@ -108,7 +114,7 @@ const Inventory_Details_Modal: React.FC<Inventory_Details_Modal_Interface> = ({
</Link>

<button
onClick={() => deleteItemClean(itemData.id || 0)}
onClick={() => deleteHelper(itemData.id || 0, onClose)}
className="modal-action-button secondary"
>
Delete
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @author sylee212
// src/components/card_organization_inventory_status_card.tsx

/*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @author sylee212
// src/components/ui/card_organization_inventory_status_data.tsx

/*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @author sylee212
// src/components/card_organization_inventory_details_modal.tsx

/*
Expand Down Expand Up @@ -123,6 +124,7 @@ export const Member_Details_Modal: React.FC<Member_Details_Modal_Interface> = ({
<div className="modal-action-button secondary">Modify</div>
</Link>

{/* // PENDING */}
<button
onClick={() => deleteItemClean(itemData.id || 0)}
className="modal-action-button secondary"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @author sylee212
import React from "react";

import { Member_Details_Interface } from "./card_organization_member_details_modal";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @author sylee212
// src/components/card_organization_recent_activities_item.tsx

import React from "react";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @author sylee212
// src/components/card_organization_recent_activities_panel.tsx

/*
Expand Down
1 change: 1 addition & 0 deletions client/src/components/ui/card_organization_statistics.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @author sylee212
// src/components/ui/Statistics_Card.tsx

import React from "react";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @author sylee212
import React from "react";

import { Inventory_Details_Interface } from "./card_organization_inventory_details_modal";
Expand Down
Loading
Loading