From 69a2023ec13f68e61737c6905170d4a59d461ade Mon Sep 17 00:00:00 2001 From: root Date: Wed, 10 Jun 2026 18:24:58 +0000 Subject: [PATCH] feat: suggestions panel for employees + admin review --- src/components/admin/SuggestionsPanel.jsx | 259 ++++++++++++++++++++++ src/pages/DashboardPage.jsx | 5 + 2 files changed, 264 insertions(+) create mode 100644 src/components/admin/SuggestionsPanel.jsx diff --git a/src/components/admin/SuggestionsPanel.jsx b/src/components/admin/SuggestionsPanel.jsx new file mode 100644 index 0000000..6acebfa --- /dev/null +++ b/src/components/admin/SuggestionsPanel.jsx @@ -0,0 +1,259 @@ +import React from "react"; +import { Panel } from "../UI/Panel"; +import { useAuth } from "../../context/AuthContext"; + +const CATEGORY_OPTIONS = [ + { value: "feature", label: "Новая функция", icon: "✨" }, + { value: "improvement", label: "Улучшение", icon: "🔧" }, + { value: "bug", label: "Проблема", icon: "🐛" }, + { value: "other", label: "Другое", icon: "💬" }, +]; + +const STATUS_MAP = { + new: { label: "Новое", color: "#3b82f6" }, + reviewed: { label: "Рассмотрено", color: "#f59e0b" }, + accepted: { label: "Принято", color: "#22c55e" }, + declined: { label: "Отклонено", color: "#ef4444" }, + implemented: { label: "Реализовано", color: "#8b5cf6" }, +}; + +export const SuggestionsPanel = () => { + const { user } = useAuth(); + const isAdmin = ["admin", "mega_admin"].includes(user?.role); + const [suggestions, setSuggestions] = React.useState([]); + const [loading, setLoading] = React.useState(true); + const [newText, setNewText] = React.useState(""); + const [newCategory, setNewCategory] = React.useState("feature"); + const [submitting, setSubmitting] = React.useState(false); + const [message, setMessage] = React.useState(""); + + const getSupabase = React.useCallback(async () => { + const { createClient } = await import("@supabase/supabase-js"); + return createClient( + import.meta.env.VITE_SUPABASE_URL || window.__SUPABASE_URL__, + import.meta.env.VITE_SUPABASE_ANON_KEY || window.__SUPABASE_ANON_KEY__ + ); + }, []); + + const fetchSuggestions = React.useCallback(async () => { + try { + const supabase = await getSupabase(); + const { data } = await supabase + .from("suggestions") + .select("*") + .order("created_at", { ascending: false }); + setSuggestions(data || []); + } catch (e) { + console.error("fetch suggestions error", e); + } finally { + setLoading(false); + } + }, [getSupabase]); + + React.useEffect(() => { fetchSuggestions(); }, [fetchSuggestions]); + + const handleSubmit = async () => { + if (!newText.trim()) return; + setSubmitting(true); + try { + const supabase = await getSupabase(); + const { error } = await supabase.from("suggestions").insert({ + author_id: user?.id, + author_name: user?.name || user?.email || "Сотрудник", + author_role: user?.role || "unknown", + content: newText.trim(), + category: newCategory, + }); + if (error) throw error; + setNewText(""); + setMessage("✅ Предложение отправлено!"); + fetchSuggestions(); + setTimeout(() => setMessage(""), 3000); + } catch (e) { + setMessage("❌ Ошибка: " + e.message); + } finally { + setSubmitting(false); + } + }; + + const handleStatusChange = async (id, newStatus, adminComment) => { + try { + const supabase = await getSupabase(); + const updates = { status: newStatus, updated_at: new Date().toISOString() }; + if (adminComment !== undefined) updates.admin_comment = adminComment; + const { error } = await supabase.from("suggestions").update(updates).eq("id", id); + if (error) throw error; + fetchSuggestions(); + } catch (e) { + console.error("update suggestion error", e); + } + }; + + return ( +
+ +
+ 💡 +

+ Предложить улучшение +

+
+

+ Есть идея? Опишите — админы рассмотрят. +

+
+ {CATEGORY_OPTIONS.map((cat) => ( + + ))} +
+