add template managment
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "no-copy-admin-panel-frontend",
|
"name": "no-copy-admin-panel-frontend",
|
||||||
"version": "0.10.0",
|
"version": "0.11.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev -p 2996",
|
"dev": "next dev -p 2996",
|
||||||
|
|||||||
@@ -208,6 +208,118 @@ export async function fetchEmployeInfo() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const parsed = await response.json();
|
||||||
|
|
||||||
|
if (parsed.message_code === 0) {
|
||||||
|
return parsed.message_body;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error(`${response.status}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchStuffTemplates() {
|
||||||
|
const token = await getSessionData('token');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE_URL}/api/admin`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
version: 1,
|
||||||
|
msg_id: 200,
|
||||||
|
token: token,
|
||||||
|
message_body: {
|
||||||
|
action: 'get_all'
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const parsed = await response.json();
|
||||||
|
|
||||||
|
if (parsed.message_code === 0) {
|
||||||
|
return parsed.message_body;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error(`${response.status}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createStuffTemplates(name: string, changePassword: boolean, permissions: Permissions) {
|
||||||
|
const token = await getSessionData('token');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE_URL}/api/admin`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
version: 1,
|
||||||
|
msg_id: 200,
|
||||||
|
token: token,
|
||||||
|
message_body: {
|
||||||
|
action: 'create',
|
||||||
|
name: name,
|
||||||
|
change_password: changePassword,
|
||||||
|
permissions: permissions
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const parsed = await response.json();
|
||||||
|
|
||||||
|
if (parsed.message_code === 0) {
|
||||||
|
return parsed.message_body;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error(`${response.status}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function removeStuffTemplates(id: number,) {
|
||||||
|
const token = await getSessionData('token');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE_URL}/api/admin`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
version: 1,
|
||||||
|
msg_id: 200,
|
||||||
|
token: token,
|
||||||
|
message_body: {
|
||||||
|
action: 'delete',
|
||||||
|
id: id
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const parsed = await response.json();
|
const parsed = await response.json();
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ export default function DropDownList({ children, value, callBack, translatedValu
|
|||||||
setIsOpen(!isOpen)
|
setIsOpen(!isOpen)
|
||||||
}}
|
}}
|
||||||
className="dropdown-button"
|
className="dropdown-button"
|
||||||
|
type="button"
|
||||||
>
|
>
|
||||||
<span className="flex items-center">
|
<span className="flex items-center">
|
||||||
<span className="mr-2">
|
<span className="mr-2">
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import { fetchStuffTemplates } from '@/app/actions/stuffActions';
|
||||||
|
|
||||||
|
interface StuffTemplates {
|
||||||
|
id: number;
|
||||||
|
change_password: boolean;
|
||||||
|
name: string;
|
||||||
|
permissions: {
|
||||||
|
subscriptions: number;
|
||||||
|
mailings: number;
|
||||||
|
complaints: number;
|
||||||
|
agreements: number;
|
||||||
|
staff: number;
|
||||||
|
content_moderation: number;
|
||||||
|
users: number;
|
||||||
|
kyc: number;
|
||||||
|
money: number;
|
||||||
|
claims: number;
|
||||||
|
api: number;
|
||||||
|
dash: number;
|
||||||
|
tariffs: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useStuffTemplates = () => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ['stuffTemplates'],
|
||||||
|
queryFn: () => {
|
||||||
|
return fetchStuffTemplates()
|
||||||
|
},
|
||||||
|
select: (data: StuffTemplates[] | null) => {
|
||||||
|
if (!data) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
retry: false
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -15,6 +15,35 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
padding: 1.5rem;
|
padding: 1.5rem;
|
||||||
|
|
||||||
|
.template-name-input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 6px;
|
||||||
|
margin: 16px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-icon {
|
||||||
|
padding: 8px;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 6px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-icon:hover:not(:disabled) {
|
||||||
|
background: rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.edit-permissions-modal,
|
.edit-permissions-modal,
|
||||||
@@ -39,6 +68,13 @@
|
|||||||
padding-top: 20px;
|
padding-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.templates-section {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding: 15px;
|
||||||
|
background: #f5f5f5;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
.checkbox-label {
|
.checkbox-label {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
|||||||
@@ -4,10 +4,14 @@ import styles from '@/app/styles/module/login.module.scss'
|
|||||||
import { useActionState, useState, useEffect } from 'react';
|
import { useActionState, useState, useEffect } from 'react';
|
||||||
import { createStuff } from '@/app/actions/stuffActions';
|
import { createStuff } from '@/app/actions/stuffActions';
|
||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
import { IconEye } from '@/app/ui/icons/icons';
|
import { IconEye, IconDiscet, IconDelete } from '@/app/ui/icons/icons';
|
||||||
import { useQueryClient } from '@tanstack/react-query';
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
import { useStuffTemplates } from '@/app/hooks/react-query/stuff/useStuffTemplates';
|
||||||
|
import DropDownList from '@/app/components/dropDownList';
|
||||||
|
import { createStuffTemplates, removeStuffTemplates } from '@/app/actions/stuffActions';
|
||||||
|
import ModalWindow from '@/app/components/modalWindow';
|
||||||
|
|
||||||
const createStuffSchema = z.object({
|
const createStuffSchema = z.object({
|
||||||
fullName: z.string()
|
fullName: z.string()
|
||||||
@@ -61,6 +65,7 @@ export function CreateStaffForm({ onSuccess }: { onSuccess?: () => void }) {
|
|||||||
const t = useTranslations('Login-register-form');
|
const t = useTranslations('Login-register-form');
|
||||||
const tGeneral = useTranslations('Global');
|
const tGeneral = useTranslations('Global');
|
||||||
const tPermissions = useTranslations('Permissions');
|
const tPermissions = useTranslations('Permissions');
|
||||||
|
const { data: templates, refetch: refetchTemplates } = useStuffTemplates();
|
||||||
|
|
||||||
const [showPassword, setShowPassword] = useState(false);
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
const [changePassword, setChangePassword] = useState(true);
|
const [changePassword, setChangePassword] = useState(true);
|
||||||
@@ -80,6 +85,13 @@ export function CreateStaffForm({ onSuccess }: { onSuccess?: () => void }) {
|
|||||||
tariffs: 0,
|
tariffs: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [selectedTemplateId, setSelectedTemplateId] = useState<string>('');
|
||||||
|
const [isSavingTemplate, setIsSavingTemplate] = useState(false);
|
||||||
|
const [newTemplateName, setNewTemplateName] = useState('');
|
||||||
|
const [showSaveDialog, setShowSaveDialog] = useState(false);
|
||||||
|
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
|
||||||
|
const [templateToDelete, setTemplateToDelete] = useState<number | null>(null);
|
||||||
|
|
||||||
const permissionsConfig = [
|
const permissionsConfig = [
|
||||||
{ key: 'subscriptions', label: tPermissions('subscriptions'), description: tPermissions('subscriptions-desc') },
|
{ key: 'subscriptions', label: tPermissions('subscriptions'), description: tPermissions('subscriptions-desc') },
|
||||||
{ key: 'mailings', label: tPermissions('mailings'), description: tPermissions('mailings-desc') },
|
{ key: 'mailings', label: tPermissions('mailings'), description: tPermissions('mailings-desc') },
|
||||||
@@ -101,6 +113,10 @@ export function CreateStaffForm({ onSuccess }: { onSuccess?: () => void }) {
|
|||||||
...prev,
|
...prev,
|
||||||
[key]: value
|
[key]: value
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
if (selectedTemplateId) {
|
||||||
|
setSelectedTemplateId('');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function toggleShowPassword() {
|
function toggleShowPassword() {
|
||||||
@@ -109,6 +125,66 @@ export function CreateStaffForm({ onSuccess }: { onSuccess?: () => void }) {
|
|||||||
|
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
const applyTemplate = (templateId: string) => {
|
||||||
|
if (!templates) return;
|
||||||
|
|
||||||
|
const template = templates.find(t => t.id.toString() === templateId);
|
||||||
|
if (template) {
|
||||||
|
setPermissions(template.permissions);
|
||||||
|
setSelectedTemplateId(templateId);
|
||||||
|
toast.success(`${tGeneral('template-applied')}: ${template.name}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveCurrentAsTemplate = async () => {
|
||||||
|
if (!newTemplateName.trim()) {
|
||||||
|
toast.error(tGeneral('template-name-required'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsSavingTemplate(true);
|
||||||
|
try {
|
||||||
|
const result = await createStuffTemplates(newTemplateName, changePassword, permissions);
|
||||||
|
if (result) {
|
||||||
|
toast.success(`${tGeneral('template-saved-successfully')}: ${newTemplateName}`);
|
||||||
|
setNewTemplateName('');
|
||||||
|
setShowSaveDialog(false);
|
||||||
|
await refetchTemplates();
|
||||||
|
|
||||||
|
if (result.id) {
|
||||||
|
setSelectedTemplateId(result.id.toString());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
toast.error(tGeneral('template-save-failed'));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
toast.error(tGeneral('template-save-failed'));
|
||||||
|
} finally {
|
||||||
|
setIsSavingTemplate(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteTemplate = async () => {
|
||||||
|
if (!templateToDelete) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await removeStuffTemplates(templateToDelete);
|
||||||
|
if (result) {
|
||||||
|
toast.success(tGeneral('template-deleted-successfully'));
|
||||||
|
await refetchTemplates();
|
||||||
|
if (selectedTemplateId === templateToDelete.toString()) {
|
||||||
|
setSelectedTemplateId('');
|
||||||
|
}
|
||||||
|
setShowDeleteConfirm(false);
|
||||||
|
setTemplateToDelete(null);
|
||||||
|
} else {
|
||||||
|
toast.error(tGeneral('template-delete-failed'));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
toast.error(tGeneral('template-delete-failed'));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (state?.success) {
|
if (state?.success) {
|
||||||
toast.success(tGeneral('staff-created-successfully'));
|
toast.success(tGeneral('staff-created-successfully'));
|
||||||
@@ -123,9 +199,7 @@ export function CreateStaffForm({ onSuccess }: { onSuccess?: () => void }) {
|
|||||||
}, [state, queryClient, onSuccess, t, tGeneral]);
|
}, [state, queryClient, onSuccess, t, tGeneral]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className="create-staff-window">
|
||||||
className="create-staff-window"
|
|
||||||
>
|
|
||||||
<form
|
<form
|
||||||
className={`${styles['']}`}
|
className={`${styles['']}`}
|
||||||
action={formAction}
|
action={formAction}
|
||||||
@@ -224,6 +298,62 @@ export function CreateStaffForm({ onSuccess }: { onSuccess?: () => void }) {
|
|||||||
{tGeneral('permissions')}
|
{tGeneral('permissions')}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
|
<div className="templates-section">
|
||||||
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '10px' }}>
|
||||||
|
<h3 style={{ margin: 0 }}>{tGeneral('template-management')}</h3>
|
||||||
|
<div style={{ display: 'flex', gap: '8px' }}>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setShowSaveDialog(true)}
|
||||||
|
className="btn-icon"
|
||||||
|
title={tGeneral('save-current-as-template')}
|
||||||
|
disabled={isPending}
|
||||||
|
>
|
||||||
|
<IconDiscet />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ display: 'flex', gap: '10px', alignItems: 'center' }}>
|
||||||
|
<div style={{ flex: 1 }}>
|
||||||
|
<DropDownList
|
||||||
|
value={selectedTemplateId}
|
||||||
|
callBack={applyTemplate}
|
||||||
|
translatedValue={
|
||||||
|
selectedTemplateId && templates
|
||||||
|
? templates.find(t => t.id.toString() === selectedTemplateId)?.name
|
||||||
|
: tGeneral('select-template')
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<option value="">{tGeneral('select-template')}</option>
|
||||||
|
{templates?.map((template) => (
|
||||||
|
<option key={template.id} value={template.id.toString()}>
|
||||||
|
{template.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</DropDownList>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{selectedTemplateId && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
const template = templates?.find(t => t.id.toString() === selectedTemplateId);
|
||||||
|
if (template) {
|
||||||
|
setTemplateToDelete(template.id);
|
||||||
|
setShowDeleteConfirm(true);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className="btn-icon btn-danger"
|
||||||
|
title={tGeneral('delete-template')}
|
||||||
|
disabled={isPending}
|
||||||
|
>
|
||||||
|
<IconDelete />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="permissions-list">
|
<div className="permissions-list">
|
||||||
{permissionsConfig.map((perm) => {
|
{permissionsConfig.map((perm) => {
|
||||||
const currentValue = permissions[perm.key as keyof typeof permissions];
|
const currentValue = permissions[perm.key as keyof typeof permissions];
|
||||||
@@ -233,7 +363,7 @@ export function CreateStaffForm({ onSuccess }: { onSuccess?: () => void }) {
|
|||||||
<div className="permission-header">
|
<div className="permission-header">
|
||||||
<div className="permission-info">
|
<div className="permission-info">
|
||||||
<h4 className="permission-title">{perm.label}</h4>
|
<h4 className="permission-title">{perm.label}</h4>
|
||||||
<p className="permission-description">{perm.description}</p>
|
{/* <p className="permission-description">{perm.description}</p> */}
|
||||||
</div>
|
</div>
|
||||||
<div className="permission-controls">
|
<div className="permission-controls">
|
||||||
<div className="permission-level-buttons">
|
<div className="permission-level-buttons">
|
||||||
@@ -295,6 +425,51 @@ export function CreateStaffForm({ onSuccess }: { onSuccess?: () => void }) {
|
|||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<ModalWindow state={showSaveDialog} callBack={setShowSaveDialog}>
|
||||||
|
<div
|
||||||
|
className="min-w-100"
|
||||||
|
>
|
||||||
|
<h3>{tGeneral('save-template')}</h3>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={newTemplateName}
|
||||||
|
onChange={(e) => setNewTemplateName(e.target.value)}
|
||||||
|
placeholder={tGeneral('enter-template-name')}
|
||||||
|
className="template-name-input"
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
<div className="modal-buttons">
|
||||||
|
<button onClick={() => setShowSaveDialog(false)} className="btn btn-secondary">
|
||||||
|
{tGeneral('cancel')}
|
||||||
|
</button>
|
||||||
|
<button onClick={saveCurrentAsTemplate} disabled={isSavingTemplate} className="btn btn-primary">
|
||||||
|
{isSavingTemplate ? tGeneral('saving') : tGeneral('save')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ModalWindow>
|
||||||
|
|
||||||
|
<ModalWindow state={showDeleteConfirm} callBack={setShowDeleteConfirm}>
|
||||||
|
<h3
|
||||||
|
className="mb-4"
|
||||||
|
>
|
||||||
|
{tGeneral('delete-template-confirm')}
|
||||||
|
</h3>
|
||||||
|
<p
|
||||||
|
className="mb-4"
|
||||||
|
>
|
||||||
|
{tGeneral('delete-template-warning')}
|
||||||
|
</p>
|
||||||
|
<div className="modal-buttons">
|
||||||
|
<button onClick={() => setShowDeleteConfirm(false)} className="btn btn-secondary">
|
||||||
|
{tGeneral('cancel')}
|
||||||
|
</button>
|
||||||
|
<button onClick={deleteTemplate} className="btn btn-danger">
|
||||||
|
{tGeneral('delete')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</ModalWindow>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
/* https://icon-sets.iconify.design/ic/ */
|
/* https://icon-sets.iconify.design/ic/ */
|
||||||
|
|
||||||
import { CLIENT_STATIC_FILES_RUNTIME_WEBPACK } from 'next/dist/shared/lib/constants'
|
|
||||||
|
|
||||||
export function IconImageFile() {
|
export function IconImageFile() {
|
||||||
return (
|
return (
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" className="icon">
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" className="icon">
|
||||||
@@ -143,4 +141,12 @@ export function IconDelete() {
|
|||||||
return (
|
return (
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" className="icon"><path fill="currentColor" d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6zm2.46-7.12l1.41-1.41L12 12.59l2.12-2.12l1.41 1.41L13.41 14l2.12 2.12l-1.41 1.41L12 15.41l-2.12 2.12l-1.41-1.41L10.59 14zM15.5 4l-1-1h-5l-1 1H5v2h14V4z" /></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" className="icon"><path fill="currentColor" d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6zm2.46-7.12l1.41-1.41L12 12.59l2.12-2.12l1.41 1.41L13.41 14l2.12 2.12l-1.41 1.41L12 15.41l-2.12 2.12l-1.41-1.41L10.59 14zM15.5 4l-1-1h-5l-1 1H5v2h14V4z" /></svg>
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function IconDiscet() {
|
||||||
|
return (
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" className="icon">
|
||||||
|
<g fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M3 19V5a2 2 0 0 1 2-2h11.172a2 2 0 0 1 1.414.586l2.828 2.828A2 2 0 0 1 21 7.828V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2Z" /><path d="M8.6 9h6.8a.6.6 0 0 0 .6-.6V3.6a.6.6 0 0 0-.6-.6H8.6a.6.6 0 0 0-.6.6v4.8a.6.6 0 0 0 .6.6ZM6 13.6V21h12v-7.4a.6.6 0 0 0-.6-.6H6.6a.6.6 0 0 0-.6.6Z" /></g>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
@@ -204,7 +204,24 @@
|
|||||||
"require-password-change": "Require password change on first login",
|
"require-password-change": "Require password change on first login",
|
||||||
"permissions": "Permissions",
|
"permissions": "Permissions",
|
||||||
"creating": "Creation...",
|
"creating": "Creation...",
|
||||||
"create": "Create"
|
"create": "Create",
|
||||||
|
"template-management": "Template management",
|
||||||
|
"save-current-as-template": "Save current permissions as template",
|
||||||
|
"select-template": "Select template",
|
||||||
|
"delete-template": "Delete template",
|
||||||
|
"template-applied": "Template applied",
|
||||||
|
"template-saved-successfully": "Template saved successfully",
|
||||||
|
"template-save-failed": "Failed to save template",
|
||||||
|
"template-deleted-successfully": "Template deleted successfully",
|
||||||
|
"template-delete-failed": "Failed to delete template",
|
||||||
|
"template-name-required": "Template name is required",
|
||||||
|
"save-template": "Save template",
|
||||||
|
"enter-template-name": "Enter template name",
|
||||||
|
"saving": "Saving...",
|
||||||
|
"delete-template-confirm": "Delete template",
|
||||||
|
"delete-template-warning": "Are you sure you want to delete this template?",
|
||||||
|
"save": "Save",
|
||||||
|
"delete": "Delete"
|
||||||
},
|
},
|
||||||
"Login-register-form": {
|
"Login-register-form": {
|
||||||
"and": "and",
|
"and": "and",
|
||||||
|
|||||||
@@ -204,7 +204,24 @@
|
|||||||
"require-password-change": "Требовать смену пароля при первом входе",
|
"require-password-change": "Требовать смену пароля при первом входе",
|
||||||
"permissions": "Доступы",
|
"permissions": "Доступы",
|
||||||
"creating": "Создание...",
|
"creating": "Создание...",
|
||||||
"create": "Создать"
|
"create": "Создать",
|
||||||
|
"template-management": "Управление шаблонами",
|
||||||
|
"save-current-as-template": "Сохранить текущие разрешения как шаблон",
|
||||||
|
"select-template": "Выбрать шаблон",
|
||||||
|
"delete-template": "Удалить шаблон",
|
||||||
|
"template-applied": "Шаблон применен",
|
||||||
|
"template-saved-successfully": "Шаблон успешно сохранен",
|
||||||
|
"template-save-failed": "Не удалось сохранить шаблон",
|
||||||
|
"template-deleted-successfully": "Шаблон успешно удален",
|
||||||
|
"template-delete-failed": "Не удалось удалить шаблон",
|
||||||
|
"template-name-required": "Введите название шаблона",
|
||||||
|
"save-template": "Сохранить шаблон",
|
||||||
|
"enter-template-name": "Введите название шаблона",
|
||||||
|
"saving": "Сохранение...",
|
||||||
|
"delete-template-confirm": "Удалить шаблон",
|
||||||
|
"delete-template-warning": "Вы уверены, что хотите удалить шаблон?",
|
||||||
|
"save": "Сохранить",
|
||||||
|
"delete": "Удалить"
|
||||||
},
|
},
|
||||||
"Login-register-form": {
|
"Login-register-form": {
|
||||||
"and": "и",
|
"and": "и",
|
||||||
|
|||||||
Reference in New Issue
Block a user