From 81e73f633a6aad29ff1169099b98e7b90af9644a Mon Sep 17 00:00:00 2001 From: Ishwari Deshmukh Date: Fri, 7 Aug 2026 11:49:09 +0530 Subject: [PATCH] feat: add Crew Union Strike Management UI - Add UnionManager.jsx following the InsuranceManager.jsx thin-page pattern - Wire /studio/union route into App.jsx - Add Crew Union entry to Sidebar.jsx menu - Shows satisfaction when no strike; banner + confirm modal to settle when striking Closes #389 --- frontend/src/App.jsx | 9 ++ frontend/src/components/common/Sidebar.jsx | 5 + frontend/src/pages/studio/UnionManager.jsx | 128 +++++++++++++++++++++ 3 files changed, 142 insertions(+) create mode 100644 frontend/src/pages/studio/UnionManager.jsx diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index a969bd6b..ca8eaef6 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -52,6 +52,7 @@ import TalentAcademy from "./pages/talent/TalentAcademy"; import TrophyRoom from "./pages/awards/TrophyRoom"; import AwardsSeasonDashboard from "./pages/awards/AwardsSeasonDashboard"; import StudioUpgrades from "./pages/studio/StudioUpgrades"; +import UnionManager from "./pages/studio/UnionManager"; function App() { return ( @@ -343,6 +344,14 @@ function App() { } /> + + + + } + /> { path: "/studio/upgrades", icon: Building2, }, + { + name: "Crew Union", + path: "/studio/union", + icon: Scale, + }, { name: "Franchises", path: "/studio/franchises", diff --git a/frontend/src/pages/studio/UnionManager.jsx b/frontend/src/pages/studio/UnionManager.jsx new file mode 100644 index 00000000..7ca4750b --- /dev/null +++ b/frontend/src/pages/studio/UnionManager.jsx @@ -0,0 +1,128 @@ +import React, { useState, useEffect, useCallback } from "react"; +import axios from "../../api/axios"; +import { Scale, AlertTriangle } from "lucide-react"; + +const SETTLEMENT_FEE = 1500000; + +const UnionManager = () => { + const [status, setStatus] = useState(null); + const [loading, setLoading] = useState(true); + const [settling, setSettling] = useState(false); + const [showConfirm, setShowConfirm] = useState(false); + const [error, setError] = useState(null); + + const fetchStatus = useCallback(async () => { + try { + setError(null); + const res = await axios.get("/api/studios/union/status"); + setStatus(res.data.data || res.data); + } catch (err) { + setError(err?.response?.data?.message || "Failed to load union status."); + } finally { + setLoading(false); + } + }, []); + + useEffect(() => { + fetchStatus(); + }, [fetchStatus]); + + const handleSettle = async () => { + setSettling(true); + try { + setError(null); + await axios.post("/api/studios/union/settle"); + await fetchStatus(); + setShowConfirm(false); + } catch (err) { + setError(err?.response?.data?.message || "Failed to settle the strike."); + } finally { + setSettling(false); + } + }; + + if (loading) { + return
Loading union status...
; + } + + const isStriking = status?.isStriking; + const satisfaction = status?.satisfaction ?? status?.crewSatisfaction ?? 0; + + return ( +
+
+ +

Crew Union

+
+ + {error && ( +
+ {error} +
+ )} + + {isStriking ? ( +
+
+ +
+

+ Crew union is on strike — productions halted +

+

+ Crew satisfaction fell below the threshold and the union has + declared a strike. All active productions are paused until + the strike is settled. +

+
+
+ + +
+ ) : ( +
+

+ No active strike. Crew satisfaction is currently{" "} + {satisfaction}%. +

+
+ )} + + {showConfirm && ( +
+
+

Confirm Settlement

+

+ This will deduct ₹{SETTLEMENT_FEE.toLocaleString("en-IN")} from + your studio's cash balance and resume all halted productions. + Are you sure? +

+
+ + +
+
+
+ )} +
+ ); +}; + +export default UnionManager;