setIsMenuOpen(false)}
+ onKeyDown={e => {
+ if (e.key === 'Enter' || e.key === ' ') {
+ setIsMenuOpen(false);
+ }
+ }}
+ role="button"
+ tabIndex={0}
+ aria-label="Close menu"
+ />
+ )}
+
+ );
+};
+
+export default KitchenHeader;
diff --git a/src/components/KitchenInterfaces/KitchenHeader.module.css b/src/components/KitchenInterfaces/KitchenHeader.module.css
new file mode 100644
index 0000000000..9142906408
--- /dev/null
+++ b/src/components/KitchenInterfaces/KitchenHeader.module.css
@@ -0,0 +1,211 @@
+.headerContainer {
+ background-color: #ffffff;
+ border-bottom: 1px solid #e0e0e0;
+ padding: 0 32px;
+ height: 64px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ font-family: 'Inter', sans-serif;
+ box-shadow: 0 1px 3px rgba(0,0,0,0.05);
+ position: sticky;
+ top: 0;
+ z-index: 1000;
+}
+
+/* Left side: Logo & Brand */
+.brandSection {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+}
+
+.logoImage {
+ height: 40px;
+ width: auto;
+ border-radius: 8px; /* Slight rounding if square */
+}
+
+.brandTextContainer {
+ display: flex;
+ flex-direction: column;
+ white-space: nowrap;
+}
+
+.brandTitle {
+ font-size: 16px;
+ font-weight: 600;
+ color: #333;
+ line-height: 1.2;
+}
+
+.brandSubtitle {
+ font-size: 11px;
+ color: #888;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+}
+
+/* Center-Right: Navigation */
+.navContainer {
+ display: flex;
+ gap: 8px;
+ align-items: center;
+}
+
+.navItem {
+ text-decoration: none;
+ color: #555;
+ font-size: 14px;
+ font-weight: 500;
+ padding: 8px 16px;
+ border-radius: 20px;
+ transition: all 0.2s ease;
+ white-space: nowrap;
+}
+
+.navItem:hover {
+ background-color: #f5f5f5;
+ color: #333;
+ text-decoration: none;
+}
+
+/* Active State */
+.activeLink {
+ background-color: #e6f4ea; /* Light green background */
+ color: #2e7d32; /* Darker green text */
+ font-weight: 600;
+}
+.activeLink:hover {
+ background-color: #dcedc8;
+ color: #1b5e20;
+}
+
+/* Dropdown Arrow Indicator (simple css triangle or unicode) */
+.dropdownArrow {
+ font-size: 10px;
+ margin-left: 4px;
+ color: #777;
+}
+
+/* Mobile Menu Button */
+.menuToggle {
+ display: none;
+ background: none;
+ border: none;
+ font-size: 24px;
+ color: #333;
+ cursor: pointer;
+}
+
+/* Background Overlay */
+.overlay {
+ display: none;
+}
+
+
+/* Responsive Adjustments */
+@media (max-width: 900px) {
+ .headerContainer {
+ padding: 0 16px;
+ height: 64px; /* Fixed height again */
+ flex-wrap: nowrap; /* Prevent wrap */
+ padding-bottom: 0;
+ padding-top: 0;
+ }
+
+ .brandTextContainer {
+ white-space: nowrap;
+ }
+
+ .menuToggle {
+ display: block;
+ }
+
+ .navContainer {
+ position: absolute;
+ top: 64px;
+ left: 0;
+ width: 100%;
+ background-color: white;
+ flex-direction: column;
+ padding: 16px 0;
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
+ transform: translateY(-150%);
+ transition: transform 0.3s ease-in-out;
+ z-index: 999;
+ align-items: flex-start;
+ }
+
+ .navOpen {
+ transform: translateY(0);
+ }
+
+ .navItem {
+ width: 100%;
+ padding: 12px 24px;
+ border-radius: 0;
+ }
+
+ /* Overlay */
+ .overlay {
+ display: block;
+ position: fixed;
+ top: 64px;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0,0,0,0.3);
+ z-index: 998;
+ }
+
+ /* Prevent horizontal scrolling on container overrides */
+ .navContainer::-webkit-scrollbar {
+ display: none;
+ }
+}
+
+
+/* Dark Mode Overrides */
+.darkMode {
+ background-color: #1c2541; /* bg-space-cadet */
+ border-bottom: 1px solid #2f4157;
+ color: #fff;
+}
+
+.darkMode .brandTitle {
+ color: #e6e6e6;
+}
+
+.darkMode .brandSubtitle {
+ color: #b0b0b0;
+}
+
+.darkMode .navItem {
+ color: #b0b0b0;
+}
+
+.darkMode .navItem:hover {
+ background-color: #2f4157;
+ color: #fff;
+}
+
+.darkMode .activeLink {
+ background-color: #2e7d32;
+ color: #fff;
+}
+
+.darkMode .activeLink:hover {
+ background-color: #1b5e20;
+}
+
+.darkMode .menuToggle {
+ color: #fff;
+}
+
+@media (max-width: 900px) {
+ .darkMode .navContainer {
+ background-color: #1c2541;
+ box-shadow: 0 4px 6px rgba(0,0,0,0.5);
+ }
+}
diff --git a/src/components/KitchenInterfaces/Processing/AddProcessingProjectModal.jsx b/src/components/KitchenInterfaces/Processing/AddProcessingProjectModal.jsx
new file mode 100644
index 0000000000..7bba166132
--- /dev/null
+++ b/src/components/KitchenInterfaces/Processing/AddProcessingProjectModal.jsx
@@ -0,0 +1,206 @@
+import React, { useState, useEffect } from 'react';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { faTimes } from '@fortawesome/free-solid-svg-icons';
+import { useSelector } from 'react-redux';
+import styles from './ProcessingLandingPage.module.css';
+
+const AddProcessingProjectModal = ({ isOpen, onClose, targetSection, onSave }) => {
+ const darkMode = useSelector(state => state.theme.darkMode);
+
+ const [formData, setFormData] = useState({
+ projectName: '',
+ type: targetSection || 'canning',
+ priority: 'Low',
+ quantity: '',
+ trays: '',
+ batches: '',
+ storage: '',
+ date: new Date().toISOString().split('T')[0],
+ });
+
+ useEffect(() => {
+ if (targetSection) {
+ setFormData(prev => ({ ...prev, type: targetSection }));
+ }
+ }, [targetSection]);
+
+ if (!isOpen) return null;
+
+ const handleChange = e => {
+ const { name, value } = e.target;
+ setFormData(prev => ({ ...prev, [name]: value }));
+ };
+
+ const handleSubmit = e => {
+ e.preventDefault();
+ // Map frontend field names to backend schema
+ const payload = {
+ item_name: formData.projectName,
+ process_name: formData.type,
+ quantity: Number(formData.quantity) || 0,
+ unit: formData.unit || 'lbs',
+ batches: Number(formData.batches) || 0,
+ priority: formData.priority,
+ scheduled_date: formData.date,
+ supplies_quantity: Number(formData.trays || formData.storage) || 0,
+ supplies_type: formData.type === 'cellarStorage' ? 'bins' : 'trays',
+ };
+ onSave(payload);
+ };
+
+ return (
+
+
+
+
Schedule New Project
+
+
+
+
+
+
+ );
+};
+
+export default AddProcessingProjectModal;
diff --git a/src/components/KitchenInterfaces/Processing/ProcessingLandingPage.jsx b/src/components/KitchenInterfaces/Processing/ProcessingLandingPage.jsx
new file mode 100644
index 0000000000..57ac3be685
--- /dev/null
+++ b/src/components/KitchenInterfaces/Processing/ProcessingLandingPage.jsx
@@ -0,0 +1,308 @@
+import React, { useState, useEffect } from 'react';
+import axios from 'axios';
+import { useSelector } from 'react-redux';
+import styles from './ProcessingLandingPage.module.css';
+import ProcessingQueue from './ProcessingQueue';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { ENDPOINTS } from '../../../utils/URL';
+import {
+ faArchive,
+ faSun,
+ faSnowflake,
+ faWarehouse,
+ faPlus,
+} from '@fortawesome/free-solid-svg-icons';
+
+// Mock Data
+const DASHBOARD_METRICS = [
+ { id: 1, title: 'Items Canned', value: 245, icon: faArchive, iconClass: styles.iconPurple },
+ { id: 2, title: 'Items Dehydrated', value: 128, icon: faSun, iconClass: styles.iconOrange },
+ { id: 3, title: 'Freeze Dried', value: 67, icon: faSnowflake, iconClass: styles.iconBlue },
+ { id: 4, title: 'Cellar Storage', value: 180, icon: faWarehouse, iconClass: styles.iconGreen },
+];
+
+const PROCESSING_METHODS = [
+ { id: 'canning', name: 'Canning', total: 245, thisMonth: 45, status: 'active' },
+ { id: 'dehydration', name: 'Dehydration', total: 128, thisMonth: 28, status: 'active' },
+ { id: 'freezeDrying', name: 'Freeze Drying', total: 67, thisMonth: 12, status: 'active' },
+ { id: 'cellarStorage', name: 'Cellar Storage', total: 180, thisMonth: 52, status: 'active' },
+];
+
+const CANNING_SUPPLIES = [
+ { name: 'Quart Jars', quantity: '120 units' },
+ { name: 'Pint Jars', quantity: '85 units' },
+ { name: 'Canning Lids', quantity: '200 units' },
+];
+
+const STORAGE_SUPPLIES = [
+ { name: 'Vacuum Seal Bags (Quart)', quantity: '45 units' },
+ { name: 'Mylar Bags (Gallon)', quantity: '38 units' },
+ { name: 'Cellar Storage Bins', quantity: '12 units' },
+];
+
+const SECTIONS = [
+ 'Processing Overview',
+ 'Canning',
+ 'Dehydration',
+ 'Freeze Drying',
+ 'Cellar Storage',
+ 'Bulk Orders',
+ 'Menu Calendar',
+];
+
+import AddProcessingProjectModal from './AddProcessingProjectModal';
+// ... checks for imports
+
+const ProcessingLandingPage = () => {
+ const [activeTab, setActiveTab] = useState('Processing Overview');
+ const darkMode = useSelector(state => state.theme.darkMode);
+
+ // State for projects and error handling
+ const [projects, setProjects] = useState([]);
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState(null);
+
+ // Modal State
+ const [isModalOpen, setIsModalOpen] = useState(false);
+ const [modalTargetSection, setModalTargetSection] = useState('canning');
+
+ // Fetch Projects on Mount
+ useEffect(() => {
+ const fetchProjects = async () => {
+ setLoading(true);
+ setError(null);
+ try {
+ const url = ENDPOINTS.KITCHEN_PROCESSING_PROJECTS || '/api/kitchenandinventory/processing';
+ const response = await axios.get(url);
+ if (Array.isArray(response.data)) {
+ setProjects(response.data);
+ } else {
+ // eslint-disable-next-line no-console
+ console.warn('Received non-array data from API:', response.data);
+ setError('Cannot connect to backend: Invalid response format.');
+ }
+ } catch (err) {
+ // eslint-disable-next-line no-console
+ console.error('Error fetching processing projects:', err);
+ setError('Cannot connect to backend: Processing module api not found.');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchProjects();
+ }, []);
+
+ const getProjectsByType = type => {
+ if (!Array.isArray(projects)) return [];
+ return projects.filter(p => p.process_name === type);
+ };
+
+ const openAddModal = section => {
+ setModalTargetSection(section);
+ setIsModalOpen(true);
+ };
+
+ const handleCreateProject = async newProjectData => {
+ try {
+ const url = ENDPOINTS.KITCHEN_PROCESSING_PROJECTS || '/api/kitchenandinventory/processing';
+ await axios.post(url, newProjectData);
+
+ // Re-fetch all projects from backend to get consistent data with _id
+ const response = await axios.get(url);
+ if (Array.isArray(response.data)) {
+ setProjects(response.data);
+ }
+ setIsModalOpen(false);
+ } catch (err) {
+ // eslint-disable-next-line no-console
+ console.error('Error creating project:', err);
+ // eslint-disable-next-line no-alert
+ alert('Failed to create project. Backend execution failed.');
+ setIsModalOpen(false);
+ }
+ };
+
+ const handleAddProject = type => {
+ openAddModal(type);
+ };
+
+ const renderContent = () => {
+ if (activeTab === 'Processing Overview') {
+ return (
+
+ {/* Left Column: Processing Methods */}
+
+
+
Processing Methods
+
Items processed by method
+
+
+
+ {PROCESSING_METHODS.map(method => (
+
+
+ {method.name}
+ +{method.thisMonth} this month
+
+
+ {method.total}
+ {method.status}
+
+
+ ))}
+
+
+
+ {/* Right Column: Processing Supplies */}
+
+
+
Processing Supplies
+
Current inventory of processing materials
+
+
+
+
+
Canning Supplies
+ {CANNING_SUPPLIES.map((item, idx) => (
+
+ {item.name}
+ {item.quantity}
+
+ ))}
+
+
+
+
Storage Materials
+ {STORAGE_SUPPLIES.map((item, idx) => (
+
+ {item.name}
+ {item.quantity}
+
+ ))}
+
+
+
+
+
+
+ );
+ }
+
+ // Render Queue Sections
+ if (activeTab === 'Canning') {
+ return (
+
handleAddProject('canning')}
+ />
+ );
+ }
+ if (activeTab === 'Dehydration') {
+ return (
+ handleAddProject('dehydration')}
+ />
+ );
+ }
+ if (activeTab === 'Freeze Drying') {
+ return (
+ handleAddProject('freezeDrying')}
+ />
+ );
+ }
+ if (activeTab === 'Cellar Storage') {
+ return (
+ handleAddProject('cellarStorage')}
+ />
+ );
+ }
+
+ return (
+
+
+
+
{activeTab} Section
+
This module is currently under development.
+
+
+ );
+ };
+
+ return (
+
+
+
Kitchen & Processing
+
+ Manage food processing, preservation, bulk orders, and menu planning
+
+
+
+
+ {DASHBOARD_METRICS.map(metric => (
+
+
+ {metric.title}
+
+
+
{metric.value}
+
+ ))}
+
+
+
+ {SECTIONS.map(section => (
+
+ ))}
+
+
+ {error ? (
+
+ {error}
+
+ ) : loading ? (
+
Loading projects...
+ ) : (
+ renderContent()
+ )}
+
+
setIsModalOpen(false)}
+ targetSection={modalTargetSection}
+ onSave={handleCreateProject}
+ />
+
+ );
+};
+
+export default ProcessingLandingPage;
diff --git a/src/components/KitchenInterfaces/Processing/ProcessingLandingPage.module.css b/src/components/KitchenInterfaces/Processing/ProcessingLandingPage.module.css
new file mode 100644
index 0000000000..6aa1f01668
--- /dev/null
+++ b/src/components/KitchenInterfaces/Processing/ProcessingLandingPage.module.css
@@ -0,0 +1,789 @@
+/* Main Container */
+.container {
+ padding: 24px;
+ background-color: #ffffff; /* Clean white background */
+ font-family: 'Inter', sans-serif; /* Example modern font, adjust to match project if needed */
+ color: #333;
+ width: 100%;
+}
+
+.header {
+ margin-bottom: 24px;
+}
+
+.title {
+ font-size: 24px;
+ font-weight: 600;
+ color: #333;
+ margin: 0 0 8px 0;
+}
+
+.subtitle {
+ font-size: 14px;
+ color: #666;
+ margin: 0;
+}
+
+/* Dashboard Cards */
+.cardsContainer {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
+ gap: 16px;
+ margin-bottom: 32px;
+}
+
+.card {
+ background: white;
+ border: 1px solid #e0e0e0;
+ border-radius: 12px;
+ padding: 20px;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ box-shadow: 0 2px 4px rgba(0,0,0,0.02);
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
+}
+
+.card:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 4px 12px rgba(0,0,0,0.05);
+}
+
+.cardHeader {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ margin-bottom: 12px;
+}
+
+.cardTitle {
+ font-size: 14px;
+ font-weight: 500;
+ color: #666;
+}
+
+.cardIcon {
+ font-size: 20px;
+ /* Colors for icons matching the screenshot roughly */
+}
+
+.cardValue {
+ font-size: 32px;
+ font-weight: 700;
+ color: #333;
+ margin: 0;
+}
+
+/* Section Navbar */
+.navbar {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+ border-bottom: 1px solid #eee;
+ padding-bottom: 16px;
+ margin-bottom: 32px;
+}
+
+.navItem {
+ padding: 8px 16px;
+ border-radius: 20px;
+ font-size: 14px;
+ font-weight: 500;
+ color: #666;
+ cursor: pointer;
+ background: none;
+ border: none;
+ transition: all 0.2s;
+}
+
+.navItem:hover {
+ background-color: #f5f5f5;
+ color: #333;
+}
+
+.activeNavItem {
+ background-color: #333; /* Or a specific green from branding */
+ color: white;
+}
+.activeNavItem:hover {
+ background-color: #444;
+ color: white;
+}
+
+
+/* Processing Overview Section Layout */
+.overviewContainer {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 24px;
+}
+
+@media (max-width: 900px) {
+ .overviewContainer {
+ grid-template-columns: 1fr;
+ }
+}
+
+.sectionBox {
+ background: #fff;
+ /* border: 1px solid #e0e0e0; */
+ /* border-radius: 12px; */
+ /* padding: 24px; */
+}
+
+.sectionHeader {
+ margin-bottom: 24px;
+}
+.sectionTitle {
+ font-size: 18px;
+ font-weight: 600;
+ margin: 0 0 4px 0;
+}
+.sectionSubtitle {
+ font-size: 14px;
+ color: #666;
+ margin: 0;
+}
+
+
+/* Processing Methods List */
+.methodList {
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+}
+
+.methodItem {
+ display: flex;
+ justify-content: space-between;
+ align-items: center; /* Center vertically */
+ padding: 16px;
+ border: 1px solid #eee;
+ border-radius: 8px;
+}
+
+.methodInfo {
+ display: flex;
+ flex-direction: column;
+}
+
+.methodName {
+ font-weight: 600;
+ font-size: 16px;
+ color: #333;
+ margin-bottom: 4px;
+}
+
+.methodSub {
+ font-size: 13px;
+ color: #888;
+}
+
+.methodStats {
+ text-align: right;
+}
+
+.methodValue {
+ display: block;
+ font-size: 18px;
+ font-weight: 700;
+ color: #333;
+}
+.methodStatus {
+ font-size: 12px;
+ color: #28a745; /* Green for active */
+ background: #e6f4ea;
+ padding: 2px 8px;
+ border-radius: 12px;
+ display: inline-block;
+ margin-top: 4px;
+}
+
+/* Supplies List */
+.suppliesList {
+ width: 100%;
+}
+.supplyCategory {
+ margin-bottom: 24px;
+}
+.categoryTitle {
+ font-size: 14px;
+ font-weight: 600;
+ color: #333;
+ margin-bottom: 12px;
+}
+
+.supplyItem {
+ display: flex;
+ justify-content: space-between;
+ padding: 12px 0;
+ border-bottom: 1px solid #f0f0f0;
+ font-size: 14px;
+}
+
+.supplyName {
+ color: #666;
+}
+
+.supplyQuantity {
+ font-weight: 500;
+ color: #888;
+}
+
+.addSupplyBtn {
+ width: 100%;
+ padding: 12px;
+ border: 1px dashed #ccc;
+ background: white;
+ color: #666;
+ border-radius: 8px;
+ cursor: pointer;
+ font-weight: 500;
+ margin-top: 16px;
+ transition: all 0.2s;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+}
+.addSupplyBtn:hover {
+ border-color: #888;
+ color: #333;
+ background-color: #fafafa;
+}
+
+/* Icon colors specific classes */
+.iconPurple { color: #9c27b0; }
+.iconOrange { color: #ed6c02; }
+.iconBlue { color: #0288d1; }
+.iconGreen { color: #2e7d32; }
+
+/* Placeholder Styles */
+.placeholderContainer {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 48px;
+ background: #f9f9f9;
+ border-radius: 12px;
+ border: 1px dashed #e0e0e0;
+ min-height: 300px;
+}
+
+.placeholderContent {
+ text-align: center;
+ color: #888;
+}
+
+.placeholderIcon {
+ font-size: 48px;
+ margin-bottom: 16px;
+ color: #ccc;
+}
+
+.placeholderContent h2 {
+ font-size: 20px;
+ color: #666;
+ margin-bottom: 8px;
+}
+
+/* Dark Mode Overrides */
+.darkMode {
+ background-color: #1c1c1c; /* bg-darkmode-liblack */
+ color: #fff;
+}
+
+.darkMode .title {
+ color: #fff;
+}
+
+.darkMode .subtitle {
+ color: #aaa;
+}
+
+.darkMode .card {
+ background-color: #1c2541; /* bg-space-cadet */
+ border-color: #2f4157;
+ box-shadow: 0 2px 4px rgba(0,0,0,0.5);
+}
+
+.darkMode .card:hover {
+ box-shadow: 0 4px 12px rgba(0,0,0,0.7);
+}
+
+.darkMode .cardTitle {
+ color: #b0b0b0;
+}
+
+.darkMode .cardValue {
+ color: #fff;
+}
+
+.darkMode .navbar {
+ border-bottom-color: #444;
+}
+
+.darkMode .navItem {
+ color: #aaa;
+}
+
+.darkMode .navItem:hover {
+ background-color: #333;
+ color: #fff;
+}
+
+.darkMode .activeNavItem {
+ background-color: #ffffff !important;
+ color: #121212 !important;
+ font-weight: 600;
+}
+
+.darkMode .activeNavItem:hover {
+ background-color: #f0f0f0 !important;
+ color: #000 !important;
+}
+
+.darkMode .methodStatus {
+ background-color: rgba(40, 167, 69, 0.2);
+ color: #4cd964;
+ border: 1px solid #2ea043;
+ box-shadow: none;
+}
+
+.darkMode .sectionBox {
+ background-color: #1c1c1c;
+}
+
+.darkMode .sectionTitle {
+ color: #fff;
+}
+
+.darkMode .sectionSubtitle {
+ color: #aaa;
+}
+
+.darkMode .methodItem {
+ border-color: #444;
+}
+
+.darkMode .methodName {
+ color: #e0e0e0;
+}
+
+.darkMode .methodSub {
+ color: #888;
+}
+
+.darkMode .methodValue {
+ color: #fff;
+}
+
+.darkMode .categoryTitle {
+ color: #fff;
+}
+
+.darkMode .supplyItem {
+ border-bottom-color: #444;
+}
+
+.darkMode .supplyName {
+ color: #ccc;
+}
+
+.darkMode .supplyQuantity {
+ color: #aaa;
+}
+
+.darkMode .addSupplyBtn {
+ background-color: #2c2c2c;
+ border-color: #444;
+ color: #ddd;
+}
+
+.darkMode .addSupplyBtn:hover {
+ background-color: #333;
+ color: #fff;
+}
+
+.darkMode .placeholderContainer {
+ background-color: #2c2c2c;
+ border-color: #444;
+}
+
+.darkMode .placeholderIcon {
+ color: #555;
+}
+
+.darkMode .placeholderContent h2 {
+ color: #ddd;
+}
+
+.darkMode .placeholderContent p {
+ color: #aaa;
+}
+
+/* Project Cards */
+.projectCard {
+ background: white;
+ border: 1px solid #eee;
+ border-radius: 8px;
+ padding: 16px 20px;
+ margin-bottom: 12px;
+ display: flex;
+ flex-direction: column;
+ transition: all 0.2s;
+}
+
+.projectCard:hover {
+ box-shadow: 0 2px 8px rgba(0,0,0,0.05);
+ border-color: #ddd;
+}
+
+.projectHeader {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ margin-bottom: 12px;
+}
+
+.projectQuantity {
+ font-size: 13px;
+ color: #777;
+ margin-top: 2px;
+}
+
+.projectFooter {
+ display: flex;
+ gap: 48px;
+}
+
+.footerItem {
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+}
+
+.footerLabel {
+ font-size: 12px;
+ color: #999;
+}
+
+.footerValue {
+ font-size: 14px;
+ color: #333;
+ font-weight: 500;
+}
+
+.projectName {
+ font-size: 16px;
+ font-weight: 600;
+ color: #333;
+ margin: 0 0 8px 0;
+}
+
+.priorityBadge {
+ font-size: 11px;
+ padding: 4px 8px;
+ border-radius: 12px;
+ text-transform: lowercase;
+ font-weight: 500;
+ border: 1px solid transparent;
+}
+
+.priorityHigh {
+ background-color: #ffebee;
+ color: #c62828;
+ border-color: #ffcdd2;
+}
+
+.priorityMedium {
+ background-color: #fff3e0;
+ color: #ef6c00;
+ border-color: #ffe0b2;
+}
+
+.priorityLow {
+ background-color: #e3f2fd;
+ color: #1565c0;
+ border-color: #bbdefb;
+}
+
+.projectDetails {
+ display: flex;
+ gap: 24px;
+ align-items: center;
+}
+
+.detailItem {
+ display: flex;
+ flex-direction: column;
+ min-width: 100px;
+}
+
+.detailLabel {
+ font-size: 12px;
+ color: #888;
+ margin-bottom: 2px;
+}
+
+.detailValue {
+ font-size: 14px;
+ color: #333;
+ font-weight: 500;
+}
+
+/* Queue Header */
+.queueContainer {
+ background-color: white;
+ padding: 24px;
+ border-radius: 12px;
+ border: 1px solid #e0e0e0;
+}
+
+.queueHeader {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ margin-bottom: 24px;
+}
+
+.queueTitleGroup {
+ display: flex;
+ gap: 12px;
+}
+
+.queueIcon {
+ font-size: 24px;
+ margin-top: 4px;
+}
+
+.queueTitle {
+ font-size: 18px;
+ font-weight: 600;
+ color: #333;
+ margin: 0 0 4px 0;
+}
+
+.queueSubtitle {
+ font-size: 14px;
+ color: #666;
+ margin: 0;
+}
+
+.addProjectBtn {
+ background-color: #d35400; /* Burnt orange/brownish */
+ color: white;
+ border: none;
+ padding: 10px 20px;
+ border-radius: 6px;
+ font-size: 14px;
+ font-weight: 500;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ transition: background 0.2s;
+}
+
+.addProjectBtn:hover {
+ background-color: #e67e22;
+}
+
+/* Dark Mode Extensions */
+.darkMode .queueContainer {
+ background-color: #1c1c1c;
+ border-color: #444;
+}
+
+.darkMode .queueTitle {
+ color: #fff;
+}
+
+.darkMode .queueSubtitle {
+ color: #aaa;
+}
+
+.darkModeCard {
+ background-color: #2c2c2c !important;
+ border-color: #444 !important;
+}
+
+.darkModeCard:hover {
+ border-color: #666 !important;
+}
+
+.darkMode .projectName {
+ color: #e0e0e0;
+}
+
+.darkMode .projectQuantity {
+ color: #aaa;
+}
+
+.darkMode .footerLabel {
+ color: #888;
+}
+
+.darkMode .footerValue {
+ color: #ccc;
+}
+
+.darkMode .detailLabel {
+ color: #aaa;
+}
+
+.darkMode .detailValue {
+ color: #ccc;
+}
+
+.darkMode .priorityHigh {
+ background-color: rgba(198, 40, 40, 0.2);
+ border-color: #c62828;
+ color: #ef9a9a;
+}
+.darkMode .priorityMedium {
+ background-color: rgba(239, 108, 0, 0.2);
+ border-color: #ef6c00;
+ color: #ffcc80;
+}
+.darkMode .priorityLow {
+ background-color: rgba(21, 101, 192, 0.2);
+ border-color: #1565c0;
+ color: #90caf9;
+}
+
+/* Modal Styles */
+.modalOverlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: rgba(0, 0, 0, 0.5);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 1000;
+}
+
+.modalContent {
+ background: white;
+ padding: 24px;
+ border-radius: 8px;
+ width: 500px;
+ max-width: 90%;
+ box-shadow: 0 4px 12px rgba(0,0,0,0.15);
+}
+
+.modalHeader {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 20px;
+ border-bottom: 1px solid #eee;
+ padding-bottom: 10px;
+}
+
+.modalHeader h2 {
+ margin: 0;
+ font-size: 20px;
+ color: #333;
+}
+
+.closeButton {
+ background: none;
+ border: none;
+ font-size: 18px;
+ cursor: pointer;
+ color: #666;
+}
+
+.modalForm {
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+}
+
+.formGroup {
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+ flex: 1;
+}
+
+.formRow {
+ display: flex;
+ gap: 16px;
+}
+
+.formGroup label {
+ font-size: 13px;
+ font-weight: 500;
+ color: #555;
+}
+
+.formGroup input, .formGroup select {
+ padding: 8px 12px;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ font-size: 14px;
+}
+
+.modalActions {
+ display: flex;
+ justify-content: flex-end;
+ gap: 12px;
+ margin-top: 20px;
+}
+
+.cancelBtn {
+ background: none;
+ border: 1px solid #ddd;
+ padding: 8px 16px;
+ border-radius: 4px;
+ cursor: pointer;
+ color: #666;
+}
+
+.submitBtn {
+ background-color: #28a745;
+ color: white;
+ border: none;
+ padding: 8px 20px;
+ border-radius: 4px;
+ cursor: pointer;
+ font-weight: 500;
+}
+
+.submitBtn:hover {
+ background-color: #218838;
+}
+
+/* Dark Mode Modal */
+.darkModeModal {
+ background-color: #2c2c2c;
+ border: 1px solid #444;
+}
+
+.darkModeModal .modalHeader h2 {
+ color: #eee;
+}
+
+.darkModeModal .closeButton {
+ color: #aaa;
+}
+
+.darkModeModal .formGroup label {
+ color: #ccc;
+}
+
+.darkModeModal input, .darkModeModal select {
+ background-color: #3a3a3a;
+ border-color: #555;
+ color: #eee;
+}
+
+.darkModeModal .cancelBtn {
+ border-color: #555;
+ color: #aaa;
+}
diff --git a/src/components/KitchenInterfaces/Processing/ProcessingProjectCard.jsx b/src/components/KitchenInterfaces/Processing/ProcessingProjectCard.jsx
new file mode 100644
index 0000000000..8ed40fbfad
--- /dev/null
+++ b/src/components/KitchenInterfaces/Processing/ProcessingProjectCard.jsx
@@ -0,0 +1,58 @@
+import React from 'react';
+import { useSelector } from 'react-redux';
+import styles from './ProcessingLandingPage.module.css';
+
+const ProcessingProjectCard = ({ project }) => {
+ const darkMode = useSelector(state => state.theme.darkMode);
+
+ const getPriorityColor = priority => {
+ switch (priority?.toLowerCase()) {
+ case 'high':
+ return styles.priorityHigh;
+ case 'medium':
+ return styles.priorityMedium;
+ case 'low':
+ return styles.priorityLow;
+ default:
+ return styles.priorityLow;
+ }
+ };
+
+ const formatDate = dateStr => {
+ if (!dateStr) return 'N/A';
+ const d = new Date(dateStr);
+ return d.toISOString().split('T')[0]; // YYYY-MM-DD format like in screenshot
+ };
+
+ return (
+
+
+
+
{project.item_name}
+
+ Quantity: {project.quantity} {project.unit || 'lbs'}
+
+
+
+ {project.priority || 'Low'} priority
+
+
+
+
+ {project.batches > 0 && (
+
+ Batches
+ {project.batches} batches
+
+ )}
+
+
+ Scheduled Date
+ {formatDate(project.scheduled_date)}
+
+
+
+ );
+};
+
+export default ProcessingProjectCard;
diff --git a/src/components/KitchenInterfaces/Processing/ProcessingQueue.jsx b/src/components/KitchenInterfaces/Processing/ProcessingQueue.jsx
new file mode 100644
index 0000000000..b3fee87b7e
--- /dev/null
+++ b/src/components/KitchenInterfaces/Processing/ProcessingQueue.jsx
@@ -0,0 +1,80 @@
+import React from 'react';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import {
+ faPlus,
+ faSun,
+ faSnowflake,
+ faWarehouse,
+ faArchive,
+} from '@fortawesome/free-solid-svg-icons';
+import { useSelector } from 'react-redux';
+import styles from './ProcessingLandingPage.module.css';
+import ProcessingProjectCard from './ProcessingProjectCard';
+
+const ProcessingQueue = ({ title, subtitle, projects, onAddProject, type }) => {
+ const darkMode = useSelector(state => state.theme.darkMode);
+
+ const getIcon = type => {
+ switch (type) {
+ case 'canning':
+ return faArchive;
+ case 'dehydration':
+ return faSun;
+ case 'freezeDrying':
+ return faSnowflake;
+ case 'cellarStorage':
+ return faWarehouse;
+ default:
+ return faArchive;
+ }
+ };
+
+ const getIconClass = type => {
+ switch (type) {
+ case 'canning':
+ return styles.iconPurple;
+ case 'dehydration':
+ return styles.iconOrange;
+ case 'freezeDrying':
+ return styles.iconBlue;
+ case 'cellarStorage':
+ return styles.iconGreen;
+ default:
+ return styles.iconPurple;
+ }
+ };
+
+ return (
+
+
+
+
+
+
+
+ {projects && projects.length > 0 ? (
+ projects.map(project => (
+
+ ))
+ ) : (
+
+
No scheduled projects.
+
+ )}
+
+
+ );
+};
+
+export default ProcessingQueue;
diff --git a/src/routes.jsx b/src/routes.jsx
index 03d3e30bff..2f05f7773c 100644
--- a/src/routes.jsx
+++ b/src/routes.jsx
@@ -269,6 +269,9 @@ const JobAnalyticsPage = lazy(() =>
);
const SuggestedJobsListBuilder = lazy(() => import('./components/Collaboration/SuggestedJobsList'));
+const ProcessingLandingPage = lazy(() =>
+ import('./components/KitchenInterfaces/Processing/ProcessingLandingPage'),
+);
export default (
{/* ----- LB Dashboard Routing Starts----- */}
@@ -323,7 +326,6 @@ export default (
{/* ----- LB Dashboard Routing Ends----- */}
-
@@ -348,6 +350,11 @@ export default (
+
} />
diff --git a/src/utils/URL.js b/src/utils/URL.js
index 1569bb1d52..9ca1fc56bf 100644
--- a/src/utils/URL.js
+++ b/src/utils/URL.js
@@ -30,7 +30,7 @@ export const ENDPOINTS = {
USERS_REMOVE_PROFILE_IMAGE: `${APIEndpoint}/userProfile/profileImage/remove`,
USERS_UPDATE_PROFILE_FROM_WEBSITE: `${APIEndpoint}/userProfile/profileImage/imagefromwebsite`,
- USER_PROFILE_BASIC_INFO:source=> `${APIEndpoint}/userProfile/basicInfo/${source}`,
+ USER_PROFILE_BASIC_INFO: source => `${APIEndpoint}/userProfile/basicInfo/${source}`,
USER_AUTOCOMPLETE: searchText => `${APIEndpoint}/userProfile/autocomplete/${searchText}`,
SEARCH_USER: `${APIEndpoint}/users/search`,
TOGGLE_BIO_STATUS: userId => `${APIEndpoint}/userProfile/${userId}/toggleBio`,
@@ -170,7 +170,7 @@ export const ENDPOINTS = {
},
POPULARITY_ROLES: `${APIEndpoint}/popularity/roles`,
-ENHANCED_POPULARITY: (range, roles, start, end, includeLowVolume) => {
+ ENHANCED_POPULARITY: (range, roles, start, end, includeLowVolume) => {
let url = `${APIEndpoint}/api/popularity-enhanced/timeline?`;
if (range) url += `range=${range}&`;
if (roles && roles.length > 0) {
@@ -182,7 +182,7 @@ ENHANCED_POPULARITY: (range, roles, start, end, includeLowVolume) => {
return url.slice(0, -1);
},
ENHANCED_POPULARITY_ROLES: `${APIEndpoint}/api/popularity-enhanced/roles-enhanced`,
- ENHANCED_POPULARITY_PAIRS: (roles) =>
+ ENHANCED_POPULARITY_PAIRS: (roles) =>
`${APIEndpoint}/api/popularity-enhanced/role-pairs?roles=${encodeURIComponent(roles.join(','))}`,
// titles endpoints
@@ -514,6 +514,9 @@ ENHANCED_POPULARITY: (range, roles, start, end, includeLowVolume) => {
GET_SAVED: `${APIEndpoint}/education/student/saved-interests`,
REMOVE_INTEREST: `${APIEndpoint}/education/student/saved-interests`,
CHECK_IF_SAVED: `${APIEndpoint}/education/student/saved-interests/check`,
+
+ // Kitchen and Inventory
+ KITCHEN_PROCESSING_PROJECTS: `${APIEndpoint}/kitchenandinventory/processing`,
};
export const ApiEndpoint = APIEndpoint;
diff --git a/yarn.lock b/yarn.lock
index 66b3394b54..46cd4a0afc 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1760,10 +1760,10 @@
"@ephox/katamari" "^9.1.6"
"@ephox/sand" "^6.0.10"
-"@esbuild/win32-x64@0.25.9":
+"@esbuild/darwin-arm64@0.25.9":
version "0.25.9"
- resolved "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz"
- integrity sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==
+ resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz"
+ integrity sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==
"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0", "@eslint-community/eslint-utils@^4.7.0":
version "4.7.0"
@@ -2577,10 +2577,10 @@
resolved "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz"
integrity sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==
-"@parcel/watcher-win32-x64@2.5.1":
+"@parcel/watcher-darwin-arm64@2.5.1":
version "2.5.1"
- resolved "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz"
- integrity sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==
+ resolved "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz"
+ integrity sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==
"@parcel/watcher@^2.4.1":
version "2.5.1"
@@ -2748,10 +2748,10 @@
resolved "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz"
integrity sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==
-"@rollup/rollup-win32-x64-msvc@4.49.0":
+"@rollup/rollup-darwin-arm64@4.49.0":
version "4.49.0"
- resolved "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.49.0.tgz"
- integrity sha512-gEtqFbzmZLFk2xKh7g0Rlo8xzho8KrEFEkzvHbfUGkrgXOpZ4XagQ6n+wIZFNh1nTb8UD16J4nFSFKXYgnbdBg==
+ resolved "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.49.0.tgz"
+ integrity sha512-99kMMSMQT7got6iYX3yyIiJfFndpojBmkHfTc1rIje8VbjhmqBXE+nb7ZZP3A5skLyujvT0eIUCUsxAe6NjWbw==
"@rtsao/scc@^1.1.0":
version "1.1.0"
@@ -3459,10 +3459,10 @@
resolved "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz"
integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==
-"@unrs/resolver-binding-win32-x64-msvc@1.11.1":
+"@unrs/resolver-binding-darwin-arm64@1.11.1":
version "1.11.1"
- resolved "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz"
- integrity sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==
+ resolved "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz"
+ integrity sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==
"@vitejs/plugin-react@^4.5.0":
version "4.7.0"
@@ -6451,6 +6451,11 @@ fs.realpath@^1.0.0:
resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
+fsevents@^2.3.2, fsevents@^2.3.3, fsevents@~2.3.2, fsevents@~2.3.3:
+ version "2.3.3"
+ resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz"
+ integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
+
function-bind@^1.1.2:
version "1.1.2"
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz"
@@ -6579,7 +6584,19 @@ glob@^10.3.10:
package-json-from-dist "^1.0.0"
path-scurry "^1.11.1"
-glob@^7.1.3, glob@^7.1.4:
+glob@^7.1.3:
+ version "7.2.3"
+ resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz"
+ integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^3.1.1"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
+glob@^7.1.4:
version "7.2.3"
resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz"
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
@@ -8733,14 +8750,7 @@ p-limit@^2.2.0:
dependencies:
p-try "^2.0.0"
-p-limit@^3.0.2:
- version "3.1.0"
- resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz"
- integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
- dependencies:
- yocto-queue "^0.1.0"
-
-p-limit@^3.1.0:
+p-limit@^3.0.2, p-limit@^3.1.0:
version "3.1.0"
resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz"
integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
@@ -8999,7 +9009,16 @@ postcss-value-parser@^4.0.2, postcss-value-parser@^4.2.0:
resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz"
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
-postcss@^8.4.31, postcss@^8.5.3, postcss@^8.5.6:
+postcss@^8.4.31, postcss@8.4.49:
+ version "8.4.49"
+ resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz"
+ integrity sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==
+ dependencies:
+ nanoid "^3.3.7"
+ picocolors "^1.1.1"
+ source-map-js "^1.2.1"
+
+postcss@^8.5.3:
version "8.5.6"
resolved "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz"
integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==
@@ -9008,12 +9027,12 @@ postcss@^8.4.31, postcss@^8.5.3, postcss@^8.5.6:
picocolors "^1.1.1"
source-map-js "^1.2.1"
-postcss@8.4.49:
- version "8.4.49"
- resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz"
- integrity sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==
+postcss@^8.5.6:
+ version "8.5.6"
+ resolved "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz"
+ integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==
dependencies:
- nanoid "^3.3.7"
+ nanoid "^3.3.11"
picocolors "^1.1.1"
source-map-js "^1.2.1"