tuandesu commited on
Commit
d35c7df
·
verified ·
1 Parent(s): 5eca108

hãy tạo một trang reactjs với tính năng cho phép admin load nội dung help từ file .html hoặc txt, sau đó hiện lên UI cho user, nếu login bằng admin thì hiện link edit --> click vào link này thì mở ra dialog để admin sửa nội dung help trong một HtmlEditor

Browse files
api/helpContent.js ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import axios from 'axios';
2
+
3
+ export const getHelpContent = async () => {
4
+ return axios.get('/api/help-content');
5
+ };
6
+
7
+ export const updateHelpContent = async (content) => {
8
+ return axios.put('/api/help-content', { content });
9
+ };
10
+
11
+ export const importHelpContent = async (file) => {
12
+ const formData = new FormData();
13
+ formData.append('file', file);
14
+ return axios.post('/api/help-content/import', formData, {
15
+ headers: {
16
+ 'Content-Type': 'multipart/form-data',
17
+ },
18
+ });
19
+ };
components/HelpManagement.js ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState, useEffect } from 'react';
2
+ import axios from 'axios';
3
+ import { ToastContainer, toast } from 'react-toastify';
4
+ import 'react-toastify/dist/ReactToastify.css';
5
+ import ReactQuill from 'react-quill';
6
+ import 'react-quill/dist/quill.snow.css';
7
+
8
+ const HelpManagement = ({ isAdmin }) => {
9
+ const [helpContent, setHelpContent] = useState('');
10
+ const [isEditing, setIsEditing] = useState(false);
11
+ const [file, setFile] = useState(null);
12
+
13
+ useEffect(() => {
14
+ fetchHelpContent();
15
+ }, []);
16
+
17
+ const fetchHelpContent = async () => {
18
+ try {
19
+ const response = await axios.get('/api/help-content');
20
+ setHelpContent(response.data.content);
21
+ } catch (error) {
22
+ toast.error('Failed to load help content');
23
+ }
24
+ };
25
+
26
+ const handleFileUpload = (e) => {
27
+ const file = e.target.files[0];
28
+ if (file) {
29
+ setFile(file);
30
+ }
31
+ };
32
+
33
+ const handleImport = async () => {
34
+ if (!file) return;
35
+
36
+ try {
37
+ const formData = new FormData();
38
+ formData.append('file', file);
39
+
40
+ const response = await axios.post('/api/help-content/import', formData, {
41
+ headers: {
42
+ 'Content-Type': 'multipart/form-data',
43
+ },
44
+ });
45
+
46
+ setHelpContent(response.data.content);
47
+ toast.success('Help content imported successfully');
48
+ setFile(null);
49
+ } catch (error) {
50
+ toast.error('Failed to import help content');
51
+ }
52
+ };
53
+
54
+ const handleSave = async () => {
55
+ try {
56
+ await axios.put('/api/help-content', { content: helpContent });
57
+ toast.success('Help content saved successfully');
58
+ setIsEditing(false);
59
+ } catch (error) {
60
+ toast.error('Failed to save help content');
61
+ }
62
+ };
63
+
64
+ return (
65
+ <div className="container mx-auto p-6">
66
+ <div className="bg-white dark:bg-gray-800 rounded-lg shadow-lg p-6">
67
+ <div className="flex justify-between items-center mb-6">
68
+ <h2 className="text-2xl font-bold text-gray-800 dark:text-white">Help Center</h2>
69
+ {isAdmin && (
70
+ <div className="flex space-x-3">
71
+ {!isEditing ? (
72
+ <button
73
+ onClick={() => setIsEditing(true)}
74
+ className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition"
75
+ >
76
+ Edit Content
77
+ </button>
78
+ ) : (
79
+ <button
80
+ onClick={handleSave}
81
+ className="px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition"
82
+ >
83
+ Save Changes
84
+ </button>
85
+ )}
86
+ </div>
87
+ )}
88
+ </div>
89
+
90
+ {isEditing ? (
91
+ <div className="space-y-4">
92
+ <div className="mb-4">
93
+ <ReactQuill
94
+ theme="snow"
95
+ value={helpContent}
96
+ onChange={setHelpContent}
97
+ modules={{
98
+ toolbar: [
99
+ [{ header: [1, 2, false] }],
100
+ ['bold', 'italic', 'underline', 'strike', 'blockquote'],
101
+ [{ list: 'ordered' }, { list: 'bullet' }],
102
+ ['link', 'image'],
103
+ ['clean'],
104
+ ],
105
+ }}
106
+ />
107
+ </div>
108
+
109
+ <div className="border-t pt-4">
110
+ <h3 className="text-lg font-medium text-gray-800 dark:text-white mb-3">Import from File</h3>
111
+ <div className="flex items-center space-x-3">
112
+ <input
113
+ type="file"
114
+ accept=".html,.txt"
115
+ onChange={handleFileUpload}
116
+ className="block w-full text-sm text-gray-500
117
+ file:mr-4 file:py-2 file:px-4
118
+ file:rounded-md file:border-0
119
+ file:text-sm file:font-semibold
120
+ file:bg-blue-50 file:text-blue-700
121
+ hover:file:bg-blue-100"
122
+ />
123
+ <button
124
+ onClick={handleImport}
125
+ disabled={!file}
126
+ className={`px-4 py-2 rounded-lg ${file ? 'bg-blue-600 hover:bg-blue-700' : 'bg-gray-400'} text-white transition`}
127
+ >
128
+ Import
129
+ </button>
130
+ </div>
131
+ </div>
132
+ </div>
133
+ ) : (
134
+ <div
135
+ className="prose dark:prose-invert max-w-none"
136
+ dangerouslySetInnerHTML={{ __html: helpContent }}
137
+ />
138
+ )}
139
+ </div>
140
+ <ToastContainer position="bottom-right" autoClose={3000} />
141
+ </div>
142
+ );
143
+ };
144
+
145
+ export default HelpManagement;
pages/HelpPage.js ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react';
2
+ import HelpManagement from '../components/HelpManagement';
3
+ import { useAuth } from '../context/AuthContext';
4
+
5
+ const HelpPage = () => {
6
+ const { user } = useAuth();
7
+
8
+ return (
9
+ <div className="min-h-screen bg-gray-100 dark:bg-gray-900">
10
+ <HelpManagement isAdmin={user?.role === 'admin'} />
11
+ </div>
12
+ );
13
+ };
14
+
15
+ export default HelpPage;