Skip to content
Open
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
98 changes: 98 additions & 0 deletions app/api/filters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import axios from 'axios';

const API_BASE_URL = 'http://localhost:3001/api/filters';

// Fetch available modules based on selected unit and location IDs
export const fetchModules = async (
unitIds?: number[],
locationIds?: number[]
): Promise<{ id: number; title: string }[]> => {
try {

const queryParams = {
...(unitIds?.length ? { unitIds: unitIds.join(",") } : {}),
...(locationIds?.length ? { locationIds: locationIds.join(",") } : {}),
};
// Make a GET request to retrieve available modules
const response = await axios.get(`${API_BASE_URL}/modules`, {
params: queryParams,
});

// Return the modules result: { modules: Array<{ id: number, title: string }> }
return response.data.modules;
} catch (error) {
console.error("Error fetching modules:", error);
throw new Error("Failed to fetch modules");
}
};

// Fetch available units based on selected module and location IDs
export const fetchUnits = async (
moduleIds?: number[],
locationIds?: number[]
): Promise<{ id: number; name: string }[]> => {
try {

const queryParams = {
...(moduleIds?.length ? { moduleIds: moduleIds.join(",") } : {}),
...(locationIds?.length ? { locationIds: locationIds.join(",") } : {}),
};
// Make a GET request to retrieve available units
const response = await axios.get(`${API_BASE_URL}/units`, {
params: queryParams,
});
// Return the units result: { units: Array<{ id: number, name: string }> }
return response.data.units;
} catch (error) {
console.error("Error fetching units:", error);
throw new Error("Failed to fetch units");
}
};

// Fetch available locations based on selected module and unit IDs
export const fetchLocations = async (
moduleIds?: number[],
unitIds?: number[]
): Promise<{ id: number; name: string }[]> => {
try {

const queryParams = {
...(moduleIds?.length ? { moduleIds: moduleIds.join(",") } : {}),
...(unitIds?.length ? { unitIds: unitIds.join(",") } : {}),
};

// Make a GET request to retrieve locations
const response = await axios.get(`${API_BASE_URL}/locations`, {
params: queryParams,
});

// Return the locations result: { locations: Array<{ id: number, name: string }> }
return response.data.locations;
} catch (error) {
console.error("Error fetching locations:", error);
throw new Error("Failed to fetch locations");
}
};

// Validate the selected filters
export const validateFilters = async (
moduleIds: number[],
unitIds: number[],
locationIds: number[]
) => {
try {
// Make a POST request to validate the selected filters
const response = await axios.post(`${API_BASE_URL}/validate`, {
moduleIds,
unitIds,
locationIds,
});

// Return the validation result: { valid: boolean, errors?: string[] }
return response.data;
} catch (error) {
console.error('Error validating filters:', error);
throw new Error('Failed to validate filters');
}
};

45 changes: 45 additions & 0 deletions app/components/MultiSelect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
interface MultiSelectProps {
options: { label: string; value: number }[];
selected: number[];
onChange: (selected: number[]) => void;
disabled?: boolean;
}

const MultiSelect: React.FC<MultiSelectProps> = ({
options,
selected,
onChange,
disabled = false
}) => {
const handleSelect = (value: number) => {
if (disabled) return;

if (selected.includes(value)) {
onChange(selected.filter((item) => item !== value));
} else {
onChange([...selected, value]);
}
};

return (
<div className={disabled ? 'opacity-50' : ''}>
{options.map((option) => (
<label
key={option.value}
style={{ display: "block", margin: "5px 0" }}
className={disabled ? 'cursor-not-allowed' : 'cursor-pointer'}
>
<input
type="checkbox"
checked={selected.includes(option.value)}
onChange={() => handleSelect(option.value)}
disabled={disabled}
/>
<span className="ml-2">{option.label}</span>
</label>
))}
</div>
);
};

export default MultiSelect;
75 changes: 74 additions & 1 deletion app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,78 @@
body {
color: var(--foreground);
background: var(--background);
font-family: Arial, Helvetica, sans-serif;
font-family: Arial, sans-serif;
margin: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}

.container {
width: 90%;
max-width: 800px;
background-color: #f9f9f9;
border-radius: 8px;
padding: 20px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
}

h1 {
font-size: 1.75rem;
text-align: center;
margin-bottom: 20px;
color: var(--foreground);
}

.filter-group {
margin-bottom: 20px;
}

.filter-group h3 {
font-size: 1.25rem;
margin-bottom: 10px;
color: var(--foreground);
}

button {
background-color: #0070f3;
color: white;
border: none;
padding: 10px 15px;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s ease;
font-size: 1rem;
}

button:hover {
background-color: #005bb5;
}

button:disabled {
background-color: #cccccc;
cursor: not-allowed;
}

button+button {
margin-left: 10px;
}

.validation-message {
text-align: center;
font-weight: bold;
margin-top: 20px;
padding: 10px;
border-radius: 5px;
}

.validation-message.success {
color: #28a745;
background-color: #d4edda;
}

.validation-message.error {
color: #dc3545;
background-color: #f8d7da;
}
Loading