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) => (
+
+ ))}
+
+
+
+
+
+ Все предложения
+
+ {loading ? (
+ Загрузка...
+ ) : suggestions.length === 0 ? (
+ Пока нет предложений
+ ) : (
+
+ {suggestions.map((s) => {
+ const cat = CATEGORY_OPTIONS.find((c) => c.value === s.category) || CATEGORY_OPTIONS[3];
+ const st = STATUS_MAP[s.status] || STATUS_MAP.new;
+ return (
+
+ );
+ })}
+
+ )}
+
+
+ );
+};
+
+const SuggestionCard = ({ suggestion: s, category, status, isAdmin, onStatusChange }) => {
+ const [editing, setEditing] = React.useState(false);
+ const [comment, setComment] = React.useState(s.admin_comment || "");
+
+ const formatDate = (d) => {
+ if (!d) return "";
+ const date = new Date(d);
+ return date.toLocaleDateString("ru-RU", { day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" });
+ };
+
+ return (
+
+
+
+ {category.icon}
+ {category.label}
+
+ {status.label}
+
+
+
{formatDate(s.created_at)}
+
+
{s.content}
+
+ {s.author_name}
+ ·
+ {s.author_role}
+
+ {s.admin_comment && (
+
+ 💬 Админ: {s.admin_comment}
+
+ )}
+ {isAdmin && (
+
+
+ {Object.entries(STATUS_MAP).map(([key, val]) => (
+
+ ))}
+
+ {editing ? (
+
+ setComment(e.target.value)}
+ placeholder="Комментарий админа..."
+ className="flex-1 rounded-lg border border-[var(--color-border)] bg-[var(--color-bg)] px-3 py-1.5 text-xs text-[var(--color-text)]"
+ />
+
+
+ ) : (
+
+ )}
+
+ )}
+
+ );
+};
\ No newline at end of file
diff --git a/src/pages/DashboardPage.jsx b/src/pages/DashboardPage.jsx
index 21d39ac..0ca584b 100644
--- a/src/pages/DashboardPage.jsx
+++ b/src/pages/DashboardPage.jsx
@@ -8,6 +8,7 @@ import UserManagementPanel from "../components/admin/UserManagementPanel";
import ErrorLogPanel from "../components/admin/ErrorLogPanel";
import { StopWordsPanel } from "../components/admin/StopWordsPanel";
import { ActionLogPanel } from "../components/admin/ActionLogPanel";
+import { SuggestionsPanel } from "../components/admin/SuggestionsPanel";
import { Panel } from "../components/UI/Panel";
import { useAuth } from "../context/AuthContext";
import { useNotifications } from "../hooks/useNotifications";
@@ -23,6 +24,7 @@ const MEGA_ADMIN_NAV = [
{ key: "errors", label: "Ошибки", description: "Журнал ошибок приложения.", badge: null },
{ key: "stop_words", label: "Стоп-слова", description: "Слова, исключаемые из клиентской карточки.", badge: null },
{ key: "action_log", label: "Журнал", description: "Журнал действий сотрудников.", badge: null },
+ { key: "suggestions", label: "Предложения", description: "Предложения сотрудников по улучшению.", badge: null },
];
const ROLE_SECTION = {
@@ -106,6 +108,7 @@ export const DashboardPage = () => {
{ key: "stop_words", label: "Стоп-слова", description: "Слова, исключаемые из карточки.", badge: null },
{ key: "errors", label: "Ошибки", description: "Журнал ошибок приложения.", badge: null },
{ key: "action_log", label: "Журнал", description: "Журнал действий сотрудников.", badge: null },
+ { key: "suggestions", label: "Предложения", description: "Предложения сотрудников по улучшению.", badge: null },
]
: userRole === "logistician"
? [
@@ -113,6 +116,7 @@ export const DashboardPage = () => {
]
: [
{ key: section.key, label: section.label, description: section.description, badge: String(allOrderGroups.length || orderGroups.length || 0) },
+ { key: "suggestions", label: "Предложения", description: "Предложить улучшение.", badge: null },
];
const activeSectionMeta = navItems.find((n) => n.key === activeSection) || navItems[0];
@@ -140,6 +144,7 @@ const ALLOWED_DASHBOARD_ROLES = ["admin", "mega_admin", "manager", "logistician"
if (activeSection === "stop_words") return
;
if (activeSection === "errors") return
;
if (activeSection === "action_log") return ;
+ if (activeSection === "suggestions") return
;
if (userRole === "driver") {
return (