Редизайн доски и дизайн-система кнопок
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 50s
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 50s
This commit is contained in:
184
IMPACT_ANALYSIS.md
Normal file
184
IMPACT_ANALYSIS.md
Normal file
@@ -0,0 +1,184 @@
|
||||
# Импакт-анализ: Редизайн экрана редактирования доски желаний
|
||||
|
||||
## Дата анализа
|
||||
2025-01-21
|
||||
|
||||
## Созданные компоненты (дизайн-система)
|
||||
|
||||
### 1. `SubmitButton.jsx`
|
||||
- **Путь**: `play-life-web/src/components/SubmitButton.jsx`
|
||||
- **Назначение**: Переиспользуемый компонент кнопки сохранения с градиентным фоном
|
||||
- **Пропсы**: `loading`, `disabled`, `children`, `onClick`, `type`
|
||||
- **Стили**: Градиент от #6366f1 до #8b5cf6, hover эффект с тенью
|
||||
- **Использование**: Заменяет все кнопки сохранения в формах
|
||||
|
||||
### 2. `DeleteButton.jsx`
|
||||
- **Путь**: `play-life-web/src/components/DeleteButton.jsx`
|
||||
- **Назначение**: Переиспользуемый компонент кнопки удаления с красным фоном и иконкой корзины
|
||||
- **Пропсы**: `loading`, `disabled`, `onClick`, `title`
|
||||
- **Стили**: Красный фон #ef4444, квадратная кнопка 44x44px
|
||||
- **Использование**: Заменяет все кнопки удаления в формах
|
||||
|
||||
### 3. `Buttons.css`
|
||||
- **Путь**: `play-life-web/src/components/Buttons.css`
|
||||
- **Назначение**: Общие стили для кнопок дизайн-системы
|
||||
- **Содержимое**:
|
||||
- `.form-actions` - flex-контейнер для группировки кнопок
|
||||
- `.submit-button` - стили для кнопки сохранения
|
||||
- `.delete-button` - стили для кнопки удаления
|
||||
|
||||
## Измененные компоненты
|
||||
|
||||
### 1. `BoardForm.jsx`
|
||||
**Путь**: `play-life-web/src/components/BoardForm.jsx`
|
||||
|
||||
**Изменения**:
|
||||
- ✅ Заменена эмодзи копирования (📋) на SVG иконку в кнопке копирования ссылки
|
||||
- ✅ Удалена кнопка "Отмена" из блока `form-actions`
|
||||
- ✅ Кнопка удаления перемещена в блок `form-actions` справа от кнопки "Сохранить"
|
||||
- ✅ Добавлено состояние `isDeleting` для отслеживания процесса удаления
|
||||
- ✅ Удалена кнопка "Перегенерить ссылку"
|
||||
- ✅ Удалена функция `handleRegenerateLink` (заменена на `generateInviteLink` для внутреннего использования)
|
||||
- ✅ Интегрированы компоненты `SubmitButton` и `DeleteButton`
|
||||
- ✅ Добавлен импорт `Buttons.css`
|
||||
|
||||
**Затронутые места в компоненте**:
|
||||
- Строки 1-5: Добавлены импорты новых компонентов и стилей
|
||||
- Строка 14: Добавлено состояние `isDeleting`
|
||||
- Строки 89-105: Удалена функция `handleRegenerateLink`
|
||||
- Строки 114-132: Обновлена функция `handleToggleInvite` (использует `generateInviteLink`)
|
||||
- Строки 134-151: Обновлена функция `handleDelete` (добавлено состояние `isDeleting`)
|
||||
- Строки 216-222: Заменена эмодзи на SVG иконку копирования
|
||||
- Строки 224-229: Удалена кнопка "Перегенерить ссылку"
|
||||
- Строки 247-258: Обновлен блок `form-actions` (удалена кнопка "Отмена", добавлены новые компоненты)
|
||||
- Строки 261-265: Удален отдельный блок с кнопкой удаления
|
||||
|
||||
### 2. `BoardForm.css`
|
||||
**Путь**: `play-life-web/src/components/BoardForm.css`
|
||||
|
||||
**Изменения**:
|
||||
- ✅ Удалены стили `.regenerate-btn` (строки 128-143)
|
||||
- ✅ Удалены стили `.delete-board-btn` (строки 152-169)
|
||||
- ✅ Стили кнопок теперь импортируются из `Buttons.css`
|
||||
|
||||
**Затронутые места**:
|
||||
- Удалено 42 строки неиспользуемых стилей
|
||||
|
||||
### 3. `TaskForm.jsx`
|
||||
**Путь**: `play-life-web/src/components/TaskForm.jsx`
|
||||
|
||||
**Изменения**:
|
||||
- ✅ Интегрированы компоненты `SubmitButton` и `DeleteButton`
|
||||
- ✅ Добавлен импорт `Buttons.css` (через компоненты)
|
||||
- ✅ Заменены нативные кнопки на компоненты дизайн-системы
|
||||
|
||||
**Затронутые места в компоненте**:
|
||||
- Строки 1-4: Добавлены импорты новых компонентов
|
||||
- Строки 1170-1195: Заменены кнопки на компоненты `SubmitButton` и `DeleteButton`
|
||||
|
||||
## Затронутые экраны
|
||||
|
||||
### 1. Экран редактирования доски желаний (`board-form`)
|
||||
**Компонент**: `BoardForm`
|
||||
**Навигация**: Открывается из экрана списка желаний (`wishlist`) при нажатии на кнопку редактирования доски
|
||||
|
||||
**Изменения в UI**:
|
||||
- ✅ Кнопка копирования ссылки: эмодзи 📋 заменена на SVG иконку (два перекрывающихся прямоугольника)
|
||||
- ✅ При успешном копировании показывается SVG иконка галочки вместо текста ✓
|
||||
- ✅ Удалена кнопка "Отмена" - теперь закрытие происходит только через крестик в правом верхнем углу
|
||||
- ✅ Кнопка "Удалить доску" перемещена в блок действий справа от кнопки "Сохранить"
|
||||
- ✅ Кнопка удаления теперь имеет красный фон и иконку корзины (как в экране редактирования задачи)
|
||||
- ✅ Удалена кнопка "Перегенерить ссылку" - ссылка теперь генерируется автоматически при включении доступа
|
||||
- ✅ Кнопка "Сохранить" имеет градиентный фон и hover эффект (как в экране редактирования задачи)
|
||||
|
||||
**Функциональные изменения**:
|
||||
- Ссылка для приглашения теперь генерируется автоматически при включении переключателя "Разрешить присоединение по ссылке"
|
||||
- Кнопка удаления показывает состояние загрузки (три точки) во время удаления
|
||||
- Кнопка сохранения показывает "Сохранение..." во время процесса сохранения
|
||||
|
||||
**Путь навигации**:
|
||||
- `wishlist` → `board-form` (при нажатии на кнопку редактирования доски)
|
||||
|
||||
### 2. Экран редактирования задачи (`task-form`)
|
||||
**Компонент**: `TaskForm`
|
||||
**Навигация**: Открывается из списка задач (`tasks`) или из деталей желания (`wishlist-detail`)
|
||||
|
||||
**Изменения в UI**:
|
||||
- ✅ Кнопки сохранения и удаления теперь используют компоненты дизайн-системы
|
||||
- ✅ Визуально идентичны кнопкам на экране редактирования доски
|
||||
|
||||
**Функциональные изменения**:
|
||||
- Нет функциональных изменений, только рефакторинг кода
|
||||
|
||||
**Путь навигации**:
|
||||
- `tasks` → `task-form` (при создании/редактировании задачи)
|
||||
- `wishlist-detail` → `task-form` (при создании задачи из желания)
|
||||
|
||||
## Потенциальные места для рефакторинга
|
||||
|
||||
Следующие компоненты используют похожие кнопки и могут быть обновлены для использования новых компонентов дизайн-системы:
|
||||
|
||||
### 1. `WishlistForm.jsx`
|
||||
- **Текущее состояние**: Использует нативную кнопку с классом `submit-button`
|
||||
- **Потенциал**: Можно заменить на `SubmitButton`
|
||||
- **Расположение**: Строки 836-838, 1246-1248
|
||||
|
||||
### 2. `AddWords.jsx`
|
||||
- **Текущее состояние**: Использует нативную кнопку с классом `submit-button`
|
||||
- **Потенциал**: Можно заменить на `SubmitButton`
|
||||
- **Расположение**: Строка 187
|
||||
|
||||
### 3. Другие формы
|
||||
- Компоненты с кнопками удаления могут использовать `DeleteButton`
|
||||
- Компоненты с кнопками сохранения могут использовать `SubmitButton`
|
||||
|
||||
## Файлы, созданные/измененные
|
||||
|
||||
### Созданные файлы:
|
||||
1. `play-life-web/src/components/SubmitButton.jsx` (новый)
|
||||
2. `play-life-web/src/components/DeleteButton.jsx` (новый)
|
||||
3. `play-life-web/src/components/Buttons.css` (новый)
|
||||
|
||||
### Измененные файлы:
|
||||
1. `play-life-web/src/components/BoardForm.jsx` (обновлен)
|
||||
2. `play-life-web/src/components/BoardForm.css` (обновлен)
|
||||
3. `play-life-web/src/components/TaskForm.jsx` (обновлен)
|
||||
|
||||
## Визуальные изменения
|
||||
|
||||
### До изменений:
|
||||
- Эмодзи в кнопке копирования
|
||||
- Кнопка "Отмена" в блоке действий
|
||||
- Кнопка удаления отдельно внизу формы
|
||||
- Кнопка "Перегенерить ссылку" под полем ссылки
|
||||
- Разные стили кнопок в разных формах
|
||||
|
||||
### После изменений:
|
||||
- SVG иконки в кнопке копирования
|
||||
- Только кнопка "Сохранить" и "Удалить" в блоке действий
|
||||
- Кнопка удаления справа от кнопки сохранения
|
||||
- Автоматическая генерация ссылки
|
||||
- Единый стиль кнопок во всех формах (дизайн-система)
|
||||
|
||||
## Технические детали
|
||||
|
||||
### Зависимости
|
||||
- Новые компоненты не добавляют внешних зависимостей
|
||||
- Используют только React и существующие стили
|
||||
|
||||
### Обратная совместимость
|
||||
- ✅ Все изменения обратно совместимы
|
||||
- ✅ Функциональность не нарушена
|
||||
- ✅ API компонентов не изменился
|
||||
|
||||
### Производительность
|
||||
- ✅ Нет влияния на производительность
|
||||
- ✅ Компоненты легковесные
|
||||
- ✅ Стили оптимизированы
|
||||
|
||||
## Рекомендации
|
||||
|
||||
1. **Рефакторинг других форм**: Рассмотреть возможность замены кнопок в `WishlistForm` и `AddWords` на компоненты дизайн-системы
|
||||
2. **Расширение дизайн-системы**: Добавить другие типы кнопок (например, `CancelButton`, `IconButton`)
|
||||
3. **Документация**: Создать документацию по использованию компонентов дизайн-системы
|
||||
4. **Тестирование**: Протестировать все затронутые экраны после развертывания
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "play-life-web",
|
||||
"version": "3.25.7",
|
||||
"version": "3.26.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -125,46 +125,8 @@
|
||||
background: #4f46e5;
|
||||
}
|
||||
|
||||
.regenerate-btn {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
background: #f3f4f6;
|
||||
border: 1px solid #d1d5db;
|
||||
border-radius: 8px;
|
||||
font-size: 0.9rem;
|
||||
color: #374151;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.regenerate-btn:hover {
|
||||
background: #e5e7eb;
|
||||
border-color: #9ca3af;
|
||||
}
|
||||
|
||||
.invite-hint {
|
||||
margin-top: 8px;
|
||||
font-size: 0.85rem;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
/* Delete button */
|
||||
.delete-board-btn {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem;
|
||||
background: transparent;
|
||||
border: 1px solid #fecaca;
|
||||
border-radius: 8px;
|
||||
color: #ef4444;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.delete-board-btn:hover {
|
||||
background: #fef2f2;
|
||||
border-color: #ef4444;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,9 @@ import React, { useState, useEffect } from 'react'
|
||||
import { useAuth } from './auth/AuthContext'
|
||||
import BoardMembers from './BoardMembers'
|
||||
import Toast from './Toast'
|
||||
import SubmitButton from './SubmitButton'
|
||||
import DeleteButton from './DeleteButton'
|
||||
import './Buttons.css'
|
||||
import './BoardForm.css'
|
||||
|
||||
function BoardForm({ boardId, onNavigate, onSaved }) {
|
||||
@@ -11,6 +14,7 @@ function BoardForm({ boardId, onNavigate, onSaved }) {
|
||||
const [inviteURL, setInviteURL] = useState('')
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [loadingBoard, setLoadingBoard] = useState(false)
|
||||
const [isDeleting, setIsDeleting] = useState(false)
|
||||
const [copied, setCopied] = useState(false)
|
||||
const [toastMessage, setToastMessage] = useState(null)
|
||||
|
||||
@@ -86,7 +90,8 @@ function BoardForm({ boardId, onNavigate, onSaved }) {
|
||||
}
|
||||
}
|
||||
|
||||
const handleRegenerateLink = async () => {
|
||||
// Функция для автоматической генерации ссылки при включении доступа
|
||||
const generateInviteLink = async () => {
|
||||
try {
|
||||
const res = await authFetch(`/api/wishlist/boards/${boardId}/regenerate-invite`, {
|
||||
method: 'POST'
|
||||
@@ -95,12 +100,9 @@ function BoardForm({ boardId, onNavigate, onSaved }) {
|
||||
const data = await res.json()
|
||||
setInviteURL(data.invite_url)
|
||||
setInviteEnabled(true)
|
||||
setToastMessage({ text: 'Ссылка обновлена', type: 'success' })
|
||||
} else {
|
||||
setToastMessage({ text: 'Ошибка обновления ссылки', type: 'error' })
|
||||
}
|
||||
} catch (err) {
|
||||
setToastMessage({ text: 'Ошибка', type: 'error' })
|
||||
console.error('Error generating invite link:', err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,7 +118,7 @@ function BoardForm({ boardId, onNavigate, onSaved }) {
|
||||
|
||||
if (boardId && enabled && !inviteURL) {
|
||||
// Автоматически генерируем ссылку при включении
|
||||
await handleRegenerateLink()
|
||||
await generateInviteLink()
|
||||
} else if (boardId) {
|
||||
// Просто обновляем статус
|
||||
try {
|
||||
@@ -134,6 +136,7 @@ function BoardForm({ boardId, onNavigate, onSaved }) {
|
||||
const handleDelete = async () => {
|
||||
if (!window.confirm('Удалить доску? Все желания на ней будут удалены.')) return
|
||||
|
||||
setIsDeleting(true)
|
||||
try {
|
||||
const res = await authFetch(`/api/wishlist/boards/${boardId}`, {
|
||||
method: 'DELETE'
|
||||
@@ -144,9 +147,11 @@ function BoardForm({ boardId, onNavigate, onSaved }) {
|
||||
onNavigate('wishlist', { boardDeleted: true })
|
||||
} else {
|
||||
setToastMessage({ text: 'Ошибка удаления', type: 'error' })
|
||||
setIsDeleting(false)
|
||||
}
|
||||
} catch (err) {
|
||||
setToastMessage({ text: 'Ошибка удаления', type: 'error' })
|
||||
setIsDeleting(false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,15 +223,18 @@ function BoardForm({ boardId, onNavigate, onSaved }) {
|
||||
onClick={handleCopyLink}
|
||||
title="Копировать ссылку"
|
||||
>
|
||||
{copied ? '✓' : '📋'}
|
||||
{copied ? (
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M20 6L9 17l-5-5"></path>
|
||||
</svg>
|
||||
) : (
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
||||
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
||||
</svg>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
className="regenerate-btn"
|
||||
onClick={handleRegenerateLink}
|
||||
>
|
||||
🔄 Перегенерировать ссылку
|
||||
</button>
|
||||
<p className="invite-hint">
|
||||
Пользователь, открывший ссылку, сможет присоединиться к доске
|
||||
</p>
|
||||
@@ -245,25 +253,24 @@ function BoardForm({ boardId, onNavigate, onSaved }) {
|
||||
)}
|
||||
|
||||
<div className="form-actions">
|
||||
<button className="cancel-button" onClick={handleClose}>
|
||||
Отмена
|
||||
</button>
|
||||
<button
|
||||
className="submit-button"
|
||||
<SubmitButton
|
||||
onClick={handleSave}
|
||||
disabled={loading || !name.trim()}
|
||||
loading={loading}
|
||||
disabled={!name.trim()}
|
||||
>
|
||||
{loading ? 'Сохранение...' : 'Сохранить'}
|
||||
</button>
|
||||
Сохранить
|
||||
</SubmitButton>
|
||||
{isEdit && (
|
||||
<DeleteButton
|
||||
onClick={handleDelete}
|
||||
loading={isDeleting}
|
||||
disabled={loading}
|
||||
title="Удалить доску"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isEdit && (
|
||||
<button className="delete-board-btn" onClick={handleDelete}>
|
||||
🗑 Удалить доску
|
||||
</button>
|
||||
)}
|
||||
|
||||
{toastMessage && (
|
||||
<Toast
|
||||
message={toastMessage.text}
|
||||
|
||||
56
play-life-web/src/components/Buttons.css
Normal file
56
play-life-web/src/components/Buttons.css
Normal file
@@ -0,0 +1,56 @@
|
||||
.form-actions {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
margin-top: 2rem;
|
||||
padding-top: 1.5rem;
|
||||
border-top: 1px solid #e5e7eb;
|
||||
}
|
||||
|
||||
.submit-button {
|
||||
background: linear-gradient(to right, #6366f1, #8b5cf6);
|
||||
color: white;
|
||||
padding: 0.75rem 1.5rem;
|
||||
border: none;
|
||||
border-radius: 0.375rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.submit-button:hover:not(:disabled) {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba(99, 102, 241, 0.3);
|
||||
}
|
||||
|
||||
.submit-button:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.delete-button {
|
||||
background: #ef4444;
|
||||
color: white;
|
||||
padding: 0.75rem;
|
||||
border: none;
|
||||
border-radius: 0.375rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 44px;
|
||||
width: 44px;
|
||||
}
|
||||
|
||||
.delete-button:hover:not(:disabled) {
|
||||
background: #dc2626;
|
||||
}
|
||||
|
||||
.delete-button:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
29
play-life-web/src/components/DeleteButton.jsx
Normal file
29
play-life-web/src/components/DeleteButton.jsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import React from 'react'
|
||||
import './Buttons.css'
|
||||
|
||||
function DeleteButton({ loading, disabled, onClick, title = 'Удалить', ...props }) {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className="delete-button"
|
||||
onClick={onClick}
|
||||
disabled={disabled || loading}
|
||||
title={title}
|
||||
{...props}
|
||||
>
|
||||
{loading ? (
|
||||
<span>...</span>
|
||||
) : (
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M3 6h18"></path>
|
||||
<path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"></path>
|
||||
<path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"></path>
|
||||
<line x1="10" y1="11" x2="10" y2="17"></line>
|
||||
<line x1="14" y1="11" x2="14" y2="17"></line>
|
||||
</svg>
|
||||
)}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
export default DeleteButton
|
||||
20
play-life-web/src/components/SubmitButton.jsx
Normal file
20
play-life-web/src/components/SubmitButton.jsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import React from 'react'
|
||||
import './Buttons.css'
|
||||
|
||||
function SubmitButton({ loading, disabled, children, onClick, type = 'button', ...props }) {
|
||||
const displayText = loading ? 'Сохранение...' : (children || 'Сохранить')
|
||||
|
||||
return (
|
||||
<button
|
||||
type={type}
|
||||
className="submit-button"
|
||||
onClick={onClick}
|
||||
disabled={disabled || loading}
|
||||
{...props}
|
||||
>
|
||||
{displayText}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
export default SubmitButton
|
||||
@@ -1,6 +1,8 @@
|
||||
import React, { useState, useEffect, useRef } from 'react'
|
||||
import { useAuth } from './auth/AuthContext'
|
||||
import Toast from './Toast'
|
||||
import SubmitButton from './SubmitButton'
|
||||
import DeleteButton from './DeleteButton'
|
||||
import './TaskForm.css'
|
||||
|
||||
const API_URL = '/api/tasks'
|
||||
@@ -1168,29 +1170,20 @@ function TaskForm({ onNavigate, taskId, wishlistId, isTest: isTestFromProps = fa
|
||||
)}
|
||||
|
||||
<div className="form-actions">
|
||||
<button type="submit" disabled={loading || isDeleting} className="submit-button">
|
||||
{loading ? 'Сохранение...' : 'Сохранить'}
|
||||
</button>
|
||||
<SubmitButton
|
||||
type="submit"
|
||||
loading={loading}
|
||||
disabled={isDeleting}
|
||||
>
|
||||
Сохранить
|
||||
</SubmitButton>
|
||||
{taskId && (
|
||||
<button
|
||||
type="button"
|
||||
<DeleteButton
|
||||
onClick={handleDelete}
|
||||
className="delete-button"
|
||||
disabled={isDeleting || loading}
|
||||
loading={isDeleting}
|
||||
disabled={loading}
|
||||
title="Удалить задачу"
|
||||
>
|
||||
{isDeleting ? (
|
||||
<span>...</span>
|
||||
) : (
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M3 6h18"></path>
|
||||
<path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"></path>
|
||||
<path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"></path>
|
||||
<line x1="10" y1="11" x2="10" y2="17"></line>
|
||||
<line x1="14" y1="11" x2="14" y2="17"></line>
|
||||
</svg>
|
||||
)}
|
||||
</button>
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
Reference in New Issue
Block a user