refactor: migrace vsech formularu na FormField komponentu + JWT upgrade
- FormField.jsx: pridana podpora style prop - 23 stranek migrovano na FormField (166 vyskytu, -246 radku) - firebase/php-jwt upgrade v6.11 -> v7.0.3 (security advisory fix) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
"type": "project",
|
"type": "project",
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=8.1",
|
"php": ">=8.1",
|
||||||
"firebase/php-jwt": "^6.11",
|
"firebase/php-jwt": "^7.0",
|
||||||
"robthree/twofactorauth": "^3.0",
|
"robthree/twofactorauth": "^3.0",
|
||||||
"chillerlan/php-qrcode": "^5.0"
|
"chillerlan/php-qrcode": "^5.0"
|
||||||
},
|
},
|
||||||
|
|||||||
85
composer.lock
generated
85
composer.lock
generated
@@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "452831f603fe18144b4f4557d0c4ea01",
|
"content-hash": "081e51355832fa208c1fe8833cb07d49",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "chillerlan/php-qrcode",
|
"name": "chillerlan/php-qrcode",
|
||||||
@@ -167,16 +167,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "firebase/php-jwt",
|
"name": "firebase/php-jwt",
|
||||||
"version": "v6.11.1",
|
"version": "v7.0.3",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/firebase/php-jwt.git",
|
"url": "https://github.com/firebase/php-jwt.git",
|
||||||
"reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66"
|
"reference": "28aa0694bcfdfa5e2959c394d5a1ee7a5083629e"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/firebase/php-jwt/zipball/d1e91ecf8c598d073d0995afa8cd5c75c6e19e66",
|
"url": "https://api.github.com/repos/firebase/php-jwt/zipball/28aa0694bcfdfa5e2959c394d5a1ee7a5083629e",
|
||||||
"reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66",
|
"reference": "28aa0694bcfdfa5e2959c394d5a1ee7a5083629e",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -224,9 +224,9 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/firebase/php-jwt/issues",
|
"issues": "https://github.com/firebase/php-jwt/issues",
|
||||||
"source": "https://github.com/firebase/php-jwt/tree/v6.11.1"
|
"source": "https://github.com/firebase/php-jwt/tree/v7.0.3"
|
||||||
},
|
},
|
||||||
"time": "2025-04-09T20:32:01+00:00"
|
"time": "2026-02-25T22:16:40+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "robthree/twofactorauth",
|
"name": "robthree/twofactorauth",
|
||||||
@@ -308,77 +308,6 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-01-05T13:17:41+00:00"
|
"time": "2026-01-05T13:17:41+00:00"
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "tecnickcom/tcpdf",
|
|
||||||
"version": "6.11.2",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/tecnickcom/TCPDF.git",
|
|
||||||
"reference": "e1e2ade18e574e963473f53271591edd8c0033ec"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/e1e2ade18e574e963473f53271591edd8c0033ec",
|
|
||||||
"reference": "e1e2ade18e574e963473f53271591edd8c0033ec",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"ext-curl": "*",
|
|
||||||
"php": ">=7.1.0"
|
|
||||||
},
|
|
||||||
"type": "library",
|
|
||||||
"autoload": {
|
|
||||||
"classmap": [
|
|
||||||
"config",
|
|
||||||
"include",
|
|
||||||
"tcpdf.php",
|
|
||||||
"tcpdf_barcodes_1d.php",
|
|
||||||
"tcpdf_barcodes_2d.php",
|
|
||||||
"include/tcpdf_colors.php",
|
|
||||||
"include/tcpdf_filters.php",
|
|
||||||
"include/tcpdf_font_data.php",
|
|
||||||
"include/tcpdf_fonts.php",
|
|
||||||
"include/tcpdf_images.php",
|
|
||||||
"include/tcpdf_static.php",
|
|
||||||
"include/barcodes/datamatrix.php",
|
|
||||||
"include/barcodes/pdf417.php",
|
|
||||||
"include/barcodes/qrcode.php"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"LGPL-3.0-or-later"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Nicola Asuni",
|
|
||||||
"email": "info@tecnick.com",
|
|
||||||
"role": "lead"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "TCPDF is a PHP class for generating PDF documents and barcodes.",
|
|
||||||
"homepage": "http://www.tcpdf.org/",
|
|
||||||
"keywords": [
|
|
||||||
"PDFD32000-2008",
|
|
||||||
"TCPDF",
|
|
||||||
"barcodes",
|
|
||||||
"datamatrix",
|
|
||||||
"pdf",
|
|
||||||
"pdf417",
|
|
||||||
"qrcode"
|
|
||||||
],
|
|
||||||
"support": {
|
|
||||||
"issues": "https://github.com/tecnickcom/TCPDF/issues",
|
|
||||||
"source": "https://github.com/tecnickcom/TCPDF/tree/6.11.2"
|
|
||||||
},
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"url": "https://www.paypal.com/donate/?hosted_button_id=NZUEC5XS8MFBJ",
|
|
||||||
"type": "custom"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"time": "2026-03-03T08:58:10+00:00"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"packages-dev": [
|
"packages-dev": [
|
||||||
|
|||||||
@@ -6,13 +6,14 @@
|
|||||||
* @param {string} [error] - Chybova zprava (zobrazi se pod inputem)
|
* @param {string} [error] - Chybova zprava (zobrazi se pod inputem)
|
||||||
* @param {boolean} [required] - Zobrazi cervenu hvezdicku
|
* @param {boolean} [required] - Zobrazi cervenu hvezdicku
|
||||||
* @param {string} [className] - Extra CSS trida na wrapperu
|
* @param {string} [className] - Extra CSS trida na wrapperu
|
||||||
|
* @param {object} [style] - Inline styly na wrapperu
|
||||||
* @param {React.ReactNode} children - Input/select/textarea element
|
* @param {React.ReactNode} children - Input/select/textarea element
|
||||||
*/
|
*/
|
||||||
export default function FormField({ label, error, required, className, children }) {
|
export default function FormField({ label, error, required, className, style, children }) {
|
||||||
const groupClass = `admin-form-group${error ? ' has-error' : ''}${className ? ` ${className}` : ''}`
|
const groupClass = `admin-form-group${error ? ' has-error' : ''}${className ? ` ${className}` : ''}`
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={groupClass}>
|
<div className={groupClass} style={style}>
|
||||||
<label className={`admin-form-label${required ? ' required' : ''}`}>
|
<label className={`admin-form-label${required ? ' required' : ''}`}>
|
||||||
{label}
|
{label}
|
||||||
</label>
|
</label>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import AdminDatePicker from '../components/AdminDatePicker'
|
|||||||
import ConfirmModal from '../components/ConfirmModal'
|
import ConfirmModal from '../components/ConfirmModal'
|
||||||
import useModalLock from '../hooks/useModalLock'
|
import useModalLock from '../hooks/useModalLock'
|
||||||
import { formatTime, calculateWorkMinutes, formatMinutes } from '../utils/attendanceHelpers'
|
import { formatTime, calculateWorkMinutes, formatMinutes } from '../utils/attendanceHelpers'
|
||||||
|
import FormField from '../components/FormField'
|
||||||
import Forbidden from '../components/Forbidden'
|
import Forbidden from '../components/Forbidden'
|
||||||
import apiFetch from '../utils/api'
|
import apiFetch from '../utils/api'
|
||||||
|
|
||||||
@@ -767,8 +768,7 @@ export default function Attendance() {
|
|||||||
|
|
||||||
<div className="admin-modal-body">
|
<div className="admin-modal-body">
|
||||||
<div className="admin-form">
|
<div className="admin-form">
|
||||||
<div className="admin-form-group">
|
<FormField label="Typ nepřítomnosti">
|
||||||
<label className="admin-form-label">Typ nepřítomnosti</label>
|
|
||||||
<select
|
<select
|
||||||
value={leaveForm.leave_type}
|
value={leaveForm.leave_type}
|
||||||
onChange={(e) => setLeaveForm({ ...leaveForm, leave_type: e.target.value })}
|
onChange={(e) => setLeaveForm({ ...leaveForm, leave_type: e.target.value })}
|
||||||
@@ -778,11 +778,10 @@ export default function Attendance() {
|
|||||||
<option value="sick">Nemoc</option>
|
<option value="sick">Nemoc</option>
|
||||||
<option value="unpaid">Neplacené volno</option>
|
<option value="unpaid">Neplacené volno</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</FormField>
|
||||||
|
|
||||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem' }}>
|
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem' }}>
|
||||||
<div className="admin-form-group">
|
<FormField label="Od">
|
||||||
<label className="admin-form-label">Od</label>
|
|
||||||
<AdminDatePicker
|
<AdminDatePicker
|
||||||
mode="date"
|
mode="date"
|
||||||
value={leaveForm.date_from}
|
value={leaveForm.date_from}
|
||||||
@@ -794,16 +793,15 @@ export default function Attendance() {
|
|||||||
}))
|
}))
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Do">
|
||||||
<label className="admin-form-label">Do</label>
|
|
||||||
<AdminDatePicker
|
<AdminDatePicker
|
||||||
mode="date"
|
mode="date"
|
||||||
value={leaveForm.date_to}
|
value={leaveForm.date_to}
|
||||||
minDate={leaveForm.date_from}
|
minDate={leaveForm.date_from}
|
||||||
onChange={(val) => setLeaveForm({ ...leaveForm, date_to: val })}
|
onChange={(val) => setLeaveForm({ ...leaveForm, date_to: val })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{leaveForm.date_from && leaveForm.date_to && (
|
{leaveForm.date_from && leaveForm.date_to && (
|
||||||
@@ -832,8 +830,7 @@ export default function Attendance() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="admin-form-group">
|
<FormField label="Poznámka">
|
||||||
<label className="admin-form-label">Poznámka</label>
|
|
||||||
<textarea
|
<textarea
|
||||||
value={leaveForm.notes}
|
value={leaveForm.notes}
|
||||||
onChange={(e) => setLeaveForm({ ...leaveForm, notes: e.target.value })}
|
onChange={(e) => setLeaveForm({ ...leaveForm, notes: e.target.value })}
|
||||||
@@ -841,7 +838,7 @@ export default function Attendance() {
|
|||||||
className="admin-form-textarea"
|
className="admin-form-textarea"
|
||||||
rows={2}
|
rows={2}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import ShiftFormModal from '../components/ShiftFormModal'
|
|||||||
import AttendanceShiftTable from '../components/AttendanceShiftTable'
|
import AttendanceShiftTable from '../components/AttendanceShiftTable'
|
||||||
import useModalLock from '../hooks/useModalLock'
|
import useModalLock from '../hooks/useModalLock'
|
||||||
import useAttendanceAdmin from '../hooks/useAttendanceAdmin'
|
import useAttendanceAdmin from '../hooks/useAttendanceAdmin'
|
||||||
|
import FormField from '../components/FormField'
|
||||||
import { formatMinutes } from '../utils/attendanceHelpers'
|
import { formatMinutes } from '../utils/attendanceHelpers'
|
||||||
|
|
||||||
function getFundBarBackground(data) {
|
function getFundBarBackground(data) {
|
||||||
@@ -104,16 +105,14 @@ export default function AttendanceAdmin() {
|
|||||||
>
|
>
|
||||||
<div className="admin-card-body">
|
<div className="admin-card-body">
|
||||||
<div className="admin-form-row">
|
<div className="admin-form-row">
|
||||||
<div className="admin-form-group" style={{ marginBottom: 0 }}>
|
<FormField label="Měsíc">
|
||||||
<label className="admin-form-label">Měsíc</label>
|
|
||||||
<AdminDatePicker
|
<AdminDatePicker
|
||||||
mode="month"
|
mode="month"
|
||||||
value={month}
|
value={month}
|
||||||
onChange={(val) => setMonth(val)}
|
onChange={(val) => setMonth(val)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group" style={{ marginBottom: 0 }}>
|
<FormField label="Zaměstnanec">
|
||||||
<label className="admin-form-label">Zaměstnanec</label>
|
|
||||||
<select
|
<select
|
||||||
value={filterUserId}
|
value={filterUserId}
|
||||||
onChange={(e) => setFilterUserId(e.target.value)}
|
onChange={(e) => setFilterUserId(e.target.value)}
|
||||||
@@ -124,7 +123,7 @@ export default function AttendanceAdmin() {
|
|||||||
<option key={user.id} value={user.id}>{user.name}</option>
|
<option key={user.id} value={user.id}>{user.name}</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import Forbidden from '../components/Forbidden'
|
|||||||
import { motion, AnimatePresence } from 'framer-motion'
|
import { motion, AnimatePresence } from 'framer-motion'
|
||||||
import ConfirmModal from '../components/ConfirmModal'
|
import ConfirmModal from '../components/ConfirmModal'
|
||||||
import useModalLock from '../hooks/useModalLock'
|
import useModalLock from '../hooks/useModalLock'
|
||||||
|
import FormField from '../components/FormField'
|
||||||
|
|
||||||
import apiFetch from '../utils/api'
|
import apiFetch from '../utils/api'
|
||||||
const API_BASE = '/api/admin'
|
const API_BASE = '/api/admin'
|
||||||
@@ -594,8 +595,7 @@ export default function AttendanceBalances() {
|
|||||||
|
|
||||||
<div className="admin-modal-body">
|
<div className="admin-modal-body">
|
||||||
<div className="admin-form">
|
<div className="admin-form">
|
||||||
<div className="admin-form-group">
|
<FormField label="Nárok na dovolenou (hodiny)">
|
||||||
<label className="admin-form-label">Nárok na dovolenou (hodiny)</label>
|
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
value={editForm.vacation_total}
|
value={editForm.vacation_total}
|
||||||
@@ -605,10 +605,9 @@ export default function AttendanceBalances() {
|
|||||||
step="1"
|
step="1"
|
||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
|
|
||||||
<div className="admin-form-group">
|
<FormField label="Čerpáno dovolené (hodiny)">
|
||||||
<label className="admin-form-label">Čerpáno dovolené (hodiny)</label>
|
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
value={editForm.vacation_used}
|
value={editForm.vacation_used}
|
||||||
@@ -618,10 +617,9 @@ export default function AttendanceBalances() {
|
|||||||
step="0.5"
|
step="0.5"
|
||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
|
|
||||||
<div className="admin-form-group">
|
<FormField label="Čerpáno nemocenské (hodiny)">
|
||||||
<label className="admin-form-label">Čerpáno nemocenské (hodiny)</label>
|
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
value={editForm.sick_used}
|
value={editForm.sick_used}
|
||||||
@@ -631,7 +629,7 @@ export default function AttendanceBalances() {
|
|||||||
step="0.5"
|
step="0.5"
|
||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { useNavigate, Link } from 'react-router-dom'
|
|||||||
import { motion } from 'framer-motion'
|
import { motion } from 'framer-motion'
|
||||||
|
|
||||||
import AdminDatePicker from '../components/AdminDatePicker'
|
import AdminDatePicker from '../components/AdminDatePicker'
|
||||||
|
import FormField from '../components/FormField'
|
||||||
import apiFetch from '../utils/api'
|
import apiFetch from '../utils/api'
|
||||||
const API_BASE = '/api/admin'
|
const API_BASE = '/api/admin'
|
||||||
|
|
||||||
@@ -152,8 +153,7 @@ export default function AttendanceCreate() {
|
|||||||
<div className="admin-card-body">
|
<div className="admin-card-body">
|
||||||
<form onSubmit={handleSubmit} className="admin-form">
|
<form onSubmit={handleSubmit} className="admin-form">
|
||||||
<div className="admin-form-row">
|
<div className="admin-form-row">
|
||||||
<div className="admin-form-group">
|
<FormField label="Zaměstnanec" required>
|
||||||
<label className="admin-form-label required">Zaměstnanec</label>
|
|
||||||
<select
|
<select
|
||||||
value={form.user_id}
|
value={form.user_id}
|
||||||
onChange={(e) => setForm({ ...form, user_id: e.target.value })}
|
onChange={(e) => setForm({ ...form, user_id: e.target.value })}
|
||||||
@@ -165,20 +165,18 @@ export default function AttendanceCreate() {
|
|||||||
<option key={user.id} value={user.id}>{user.name}</option>
|
<option key={user.id} value={user.id}>{user.name}</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Datum směny" required>
|
||||||
<label className="admin-form-label required">Datum směny</label>
|
|
||||||
<AdminDatePicker
|
<AdminDatePicker
|
||||||
mode="date"
|
mode="date"
|
||||||
value={form.shift_date}
|
value={form.shift_date}
|
||||||
onChange={(val) => handleShiftDateChange(val)}
|
onChange={(val) => handleShiftDateChange(val)}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="admin-form-group">
|
<FormField label="Typ záznamu" required>
|
||||||
<label className="admin-form-label required">Typ záznamu</label>
|
|
||||||
<select
|
<select
|
||||||
value={form.leave_type}
|
value={form.leave_type}
|
||||||
onChange={(e) => setForm({ ...form, leave_type: e.target.value })}
|
onChange={(e) => setForm({ ...form, leave_type: e.target.value })}
|
||||||
@@ -190,11 +188,10 @@ export default function AttendanceCreate() {
|
|||||||
<option value="holiday">Svátek</option>
|
<option value="holiday">Svátek</option>
|
||||||
<option value="unpaid">Neplacené volno</option>
|
<option value="unpaid">Neplacené volno</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</FormField>
|
||||||
|
|
||||||
{!isWorkType && (
|
{!isWorkType && (
|
||||||
<div className="admin-form-group">
|
<FormField label="Počet hodin">
|
||||||
<label className="admin-form-label">Počet hodin</label>
|
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
value={form.leave_hours}
|
value={form.leave_hours}
|
||||||
@@ -205,98 +202,89 @@ export default function AttendanceCreate() {
|
|||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
/>
|
/>
|
||||||
<small className="admin-form-hint">Výchozí 8 hodin pro celý den</small>
|
<small className="admin-form-hint">Výchozí 8 hodin pro celý den</small>
|
||||||
</div>
|
</FormField>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isWorkType && (
|
{isWorkType && (
|
||||||
<>
|
<>
|
||||||
<div className="admin-form-row">
|
<div className="admin-form-row">
|
||||||
<div className="admin-form-group">
|
<FormField label="Příchod - datum">
|
||||||
<label className="admin-form-label">Příchod - datum</label>
|
|
||||||
<AdminDatePicker
|
<AdminDatePicker
|
||||||
mode="date"
|
mode="date"
|
||||||
value={form.arrival_date}
|
value={form.arrival_date}
|
||||||
onChange={(val) => setForm({ ...form, arrival_date: val })}
|
onChange={(val) => setForm({ ...form, arrival_date: val })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Příchod - čas">
|
||||||
<label className="admin-form-label">Příchod - čas</label>
|
|
||||||
<AdminDatePicker
|
<AdminDatePicker
|
||||||
mode="time"
|
mode="time"
|
||||||
value={form.arrival_time}
|
value={form.arrival_time}
|
||||||
onChange={(val) => setForm({ ...form, arrival_time: val })}
|
onChange={(val) => setForm({ ...form, arrival_time: val })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="admin-form-row">
|
<div className="admin-form-row">
|
||||||
<div className="admin-form-group">
|
<FormField label="Začátek pauzy - datum">
|
||||||
<label className="admin-form-label">Začátek pauzy - datum</label>
|
|
||||||
<AdminDatePicker
|
<AdminDatePicker
|
||||||
mode="date"
|
mode="date"
|
||||||
value={form.break_start_date}
|
value={form.break_start_date}
|
||||||
onChange={(val) => setForm({ ...form, break_start_date: val })}
|
onChange={(val) => setForm({ ...form, break_start_date: val })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Začátek pauzy - čas">
|
||||||
<label className="admin-form-label">Začátek pauzy - čas</label>
|
|
||||||
<AdminDatePicker
|
<AdminDatePicker
|
||||||
mode="time"
|
mode="time"
|
||||||
value={form.break_start_time}
|
value={form.break_start_time}
|
||||||
onChange={(val) => setForm({ ...form, break_start_time: val })}
|
onChange={(val) => setForm({ ...form, break_start_time: val })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="admin-form-row">
|
<div className="admin-form-row">
|
||||||
<div className="admin-form-group">
|
<FormField label="Konec pauzy - datum">
|
||||||
<label className="admin-form-label">Konec pauzy - datum</label>
|
|
||||||
<AdminDatePicker
|
<AdminDatePicker
|
||||||
mode="date"
|
mode="date"
|
||||||
value={form.break_end_date}
|
value={form.break_end_date}
|
||||||
onChange={(val) => setForm({ ...form, break_end_date: val })}
|
onChange={(val) => setForm({ ...form, break_end_date: val })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Konec pauzy - čas">
|
||||||
<label className="admin-form-label">Konec pauzy - čas</label>
|
|
||||||
<AdminDatePicker
|
<AdminDatePicker
|
||||||
mode="time"
|
mode="time"
|
||||||
value={form.break_end_time}
|
value={form.break_end_time}
|
||||||
onChange={(val) => setForm({ ...form, break_end_time: val })}
|
onChange={(val) => setForm({ ...form, break_end_time: val })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="admin-form-row">
|
<div className="admin-form-row">
|
||||||
<div className="admin-form-group">
|
<FormField label="Odchod - datum">
|
||||||
<label className="admin-form-label">Odchod - datum</label>
|
|
||||||
<AdminDatePicker
|
<AdminDatePicker
|
||||||
mode="date"
|
mode="date"
|
||||||
value={form.departure_date}
|
value={form.departure_date}
|
||||||
onChange={(val) => setForm({ ...form, departure_date: val })}
|
onChange={(val) => setForm({ ...form, departure_date: val })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Odchod - čas">
|
||||||
<label className="admin-form-label">Odchod - čas</label>
|
|
||||||
<AdminDatePicker
|
<AdminDatePicker
|
||||||
mode="time"
|
mode="time"
|
||||||
value={form.departure_time}
|
value={form.departure_time}
|
||||||
onChange={(val) => setForm({ ...form, departure_time: val })}
|
onChange={(val) => setForm({ ...form, departure_time: val })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="admin-form-group">
|
<FormField label="Poznámka">
|
||||||
<label className="admin-form-label">Poznámka</label>
|
|
||||||
<textarea
|
<textarea
|
||||||
value={form.notes}
|
value={form.notes}
|
||||||
onChange={(e) => setForm({ ...form, notes: e.target.value })}
|
onChange={(e) => setForm({ ...form, notes: e.target.value })}
|
||||||
className="admin-form-textarea"
|
className="admin-form-textarea"
|
||||||
rows={3}
|
rows={3}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
|
|
||||||
<div className="admin-form-actions">
|
<div className="admin-form-actions">
|
||||||
<Link to="/attendance/admin" className="admin-btn admin-btn-secondary">
|
<Link to="/attendance/admin" className="admin-btn admin-btn-secondary">
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import Forbidden from '../components/Forbidden'
|
|||||||
import { motion } from 'framer-motion'
|
import { motion } from 'framer-motion'
|
||||||
import AdminDatePicker from '../components/AdminDatePicker'
|
import AdminDatePicker from '../components/AdminDatePicker'
|
||||||
import { formatDate, formatDatetime, formatTime, calculateWorkMinutes, formatMinutes, getLeaveTypeName, getLeaveTypeBadgeClass, calculateWorkMinutesPrint, formatTimeOrDatetimePrint } from '../utils/attendanceHelpers'
|
import { formatDate, formatDatetime, formatTime, calculateWorkMinutes, formatMinutes, getLeaveTypeName, getLeaveTypeBadgeClass, calculateWorkMinutesPrint, formatTimeOrDatetimePrint } from '../utils/attendanceHelpers'
|
||||||
|
import FormField from '../components/FormField'
|
||||||
import apiFetch from '../utils/api'
|
import apiFetch from '../utils/api'
|
||||||
|
|
||||||
const API_BASE = '/api/admin'
|
const API_BASE = '/api/admin'
|
||||||
@@ -242,14 +243,13 @@ export default function AttendanceHistory() {
|
|||||||
>
|
>
|
||||||
<div className="admin-card-body">
|
<div className="admin-card-body">
|
||||||
<div className="admin-form-row">
|
<div className="admin-form-row">
|
||||||
<div className="admin-form-group" style={{ marginBottom: 0 }}>
|
<FormField label="Měsíc">
|
||||||
<label className="admin-form-label">Měsíc</label>
|
|
||||||
<AdminDatePicker
|
<AdminDatePicker
|
||||||
mode="month"
|
mode="month"
|
||||||
value={month}
|
value={month}
|
||||||
onChange={(val) => setMonth(val)}
|
onChange={(val) => setMonth(val)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useState, useEffect, useCallback, useRef } from 'react'
|
|||||||
import { useAlert } from '../context/AlertContext'
|
import { useAlert } from '../context/AlertContext'
|
||||||
import { useAuth } from '../context/AuthContext'
|
import { useAuth } from '../context/AuthContext'
|
||||||
import Forbidden from '../components/Forbidden'
|
import Forbidden from '../components/Forbidden'
|
||||||
|
import FormField from '../components/FormField'
|
||||||
import { motion } from 'framer-motion'
|
import { motion } from 'framer-motion'
|
||||||
|
|
||||||
import apiFetch from '../utils/api'
|
import apiFetch from '../utils/api'
|
||||||
@@ -370,82 +371,74 @@ export default function CompanySettings() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="admin-card-body">
|
<div className="admin-card-body">
|
||||||
<div className="admin-form">
|
<div className="admin-form">
|
||||||
<div className="admin-form-group">
|
<FormField label="Název firmy">
|
||||||
<label className="admin-form-label">Název firmy</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={form.company_name}
|
value={form.company_name}
|
||||||
onChange={(e) => updateField('company_name', e.target.value)}
|
onChange={(e) => updateField('company_name', e.target.value)}
|
||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-row">
|
<div className="admin-form-row">
|
||||||
<div className="admin-form-group">
|
<FormField label="Ulice">
|
||||||
<label className="admin-form-label">Ulice</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={form.street}
|
value={form.street}
|
||||||
onChange={(e) => updateField('street', e.target.value)}
|
onChange={(e) => updateField('street', e.target.value)}
|
||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Město">
|
||||||
<label className="admin-form-label">Město</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={form.city}
|
value={form.city}
|
||||||
onChange={(e) => updateField('city', e.target.value)}
|
onChange={(e) => updateField('city', e.target.value)}
|
||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
<div className="admin-form-row">
|
<div className="admin-form-row">
|
||||||
<div className="admin-form-group">
|
<FormField label="PSČ">
|
||||||
<label className="admin-form-label">PSČ</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={form.postal_code}
|
value={form.postal_code}
|
||||||
onChange={(e) => updateField('postal_code', e.target.value)}
|
onChange={(e) => updateField('postal_code', e.target.value)}
|
||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Země">
|
||||||
<label className="admin-form-label">Země</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={form.country}
|
value={form.country}
|
||||||
onChange={(e) => updateField('country', e.target.value)}
|
onChange={(e) => updateField('country', e.target.value)}
|
||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
<div className="admin-form-row">
|
<div className="admin-form-row">
|
||||||
<div className="admin-form-group">
|
<FormField label="IČO">
|
||||||
<label className="admin-form-label">IČO</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={form.company_id}
|
value={form.company_id}
|
||||||
onChange={(e) => updateField('company_id', e.target.value)}
|
onChange={(e) => updateField('company_id', e.target.value)}
|
||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="DIČ">
|
||||||
<label className="admin-form-label">DIČ</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={form.vat_id}
|
value={form.vat_id}
|
||||||
onChange={(e) => updateField('vat_id', e.target.value)}
|
onChange={(e) => updateField('vat_id', e.target.value)}
|
||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ marginTop: 4 }}>
|
<div style={{ marginTop: 4 }}>
|
||||||
<label className="admin-form-label" style={{ display: 'block', marginBottom: 4 }}>Vlastní pole</label>
|
<label className="admin-form-label" style={{ display: 'block', marginBottom: 4 }}>Vlastní pole</label>
|
||||||
{customFields.map((field, idx) => (
|
{customFields.map((field, idx) => (
|
||||||
<div key={field._key} style={{ marginBottom: 8 }}>
|
<div key={field._key} style={{ marginBottom: 8 }}>
|
||||||
<div className="admin-form-row" style={{ marginBottom: 0, alignItems: 'flex-end' }}>
|
<div className="admin-form-row" style={{ marginBottom: 0, alignItems: 'flex-end' }}>
|
||||||
<div className="admin-form-group" style={{ flex: 1 }}>
|
<FormField label={idx === 0 ? 'Název' : '\u00A0'} style={{ flex: 1 }}>
|
||||||
{idx === 0 && <label className="admin-form-label" style={{ fontSize: '0.75rem' }}>Název</label>}
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={field.name}
|
value={field.name}
|
||||||
@@ -457,9 +450,8 @@ export default function CompanySettings() {
|
|||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
placeholder="Např. Tel."
|
placeholder="Např. Tel."
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group" style={{ flex: 1 }}>
|
<FormField label={idx === 0 ? 'Hodnota' : '\u00A0'} style={{ flex: 1 }}>
|
||||||
{idx === 0 && <label className="admin-form-label" style={{ fontSize: '0.75rem' }}>Hodnota</label>}
|
|
||||||
<div style={{ display: 'flex', gap: 4, alignItems: 'center' }}>
|
<div style={{ display: 'flex', gap: 4, alignItems: 'center' }}>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
@@ -498,7 +490,7 @@ export default function CompanySettings() {
|
|||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
<label className="admin-form-checkbox" style={{ marginTop: 4 }}>
|
<label className="admin-form-checkbox" style={{ marginTop: 4 }}>
|
||||||
<input
|
<input
|
||||||
@@ -622,8 +614,7 @@ export default function CompanySettings() {
|
|||||||
</h4>
|
</h4>
|
||||||
<div className="admin-form">
|
<div className="admin-form">
|
||||||
<div className="admin-form-row">
|
<div className="admin-form-row">
|
||||||
<div className="admin-form-group">
|
<FormField label="Název účtu" required>
|
||||||
<label className="admin-form-label required">Název účtu</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={bankForm.account_name}
|
value={bankForm.account_name}
|
||||||
@@ -631,9 +622,8 @@ export default function CompanySettings() {
|
|||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
placeholder="Např. Hlavní CZK účet"
|
placeholder="Např. Hlavní CZK účet"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Název banky">
|
||||||
<label className="admin-form-label">Název banky</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={bankForm.bank_name}
|
value={bankForm.bank_name}
|
||||||
@@ -641,11 +631,10 @@ export default function CompanySettings() {
|
|||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
placeholder="Např. MONETA Money Bank, a.s."
|
placeholder="Např. MONETA Money Bank, a.s."
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
<div className="admin-form-row">
|
<div className="admin-form-row">
|
||||||
<div className="admin-form-group">
|
<FormField label="Číslo účtu">
|
||||||
<label className="admin-form-label">Číslo účtu</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={bankForm.account_number}
|
value={bankForm.account_number}
|
||||||
@@ -653,9 +642,8 @@ export default function CompanySettings() {
|
|||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
placeholder="123456789/0600"
|
placeholder="123456789/0600"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Měna">
|
||||||
<label className="admin-form-label">Měna</label>
|
|
||||||
<select
|
<select
|
||||||
value={bankForm.currency}
|
value={bankForm.currency}
|
||||||
onChange={e => setBankForm(f => ({ ...f, currency: e.target.value }))}
|
onChange={e => setBankForm(f => ({ ...f, currency: e.target.value }))}
|
||||||
@@ -666,11 +654,10 @@ export default function CompanySettings() {
|
|||||||
<option value="USD">USD</option>
|
<option value="USD">USD</option>
|
||||||
<option value="GBP">GBP</option>
|
<option value="GBP">GBP</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
<div className="admin-form-row">
|
<div className="admin-form-row">
|
||||||
<div className="admin-form-group">
|
<FormField label="IBAN">
|
||||||
<label className="admin-form-label">IBAN</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={bankForm.iban}
|
value={bankForm.iban}
|
||||||
@@ -678,9 +665,8 @@ export default function CompanySettings() {
|
|||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
placeholder="CZ65 0800 0000 1920 0014 5399"
|
placeholder="CZ65 0800 0000 1920 0014 5399"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="BIC / SWIFT">
|
||||||
<label className="admin-form-label">BIC / SWIFT</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={bankForm.bic}
|
value={bankForm.bic}
|
||||||
@@ -688,7 +674,7 @@ export default function CompanySettings() {
|
|||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
placeholder="GIBACZPX"
|
placeholder="GIBACZPX"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
<label className="admin-form-checkbox">
|
<label className="admin-form-checkbox">
|
||||||
<input
|
<input
|
||||||
@@ -833,8 +819,7 @@ export default function CompanySettings() {
|
|||||||
<div className="admin-card-body">
|
<div className="admin-card-body">
|
||||||
<div className="admin-form">
|
<div className="admin-form">
|
||||||
{/* Nabídky */}
|
{/* Nabídky */}
|
||||||
<div className="admin-form-group">
|
<FormField label="Nabídky — prefix">
|
||||||
<label className="admin-form-label">Nabídky — prefix</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={form.quotation_prefix}
|
value={form.quotation_prefix}
|
||||||
@@ -846,13 +831,12 @@ export default function CompanySettings() {
|
|||||||
<small className="admin-form-hint">
|
<small className="admin-form-hint">
|
||||||
Formát: ROK/PREFIX/ČÍSLO — ukázka: {new Date().getFullYear()}/{form.quotation_prefix || 'N'}/001
|
Formát: ROK/PREFIX/ČÍSLO — ukázka: {new Date().getFullYear()}/{form.quotation_prefix || 'N'}/001
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</FormField>
|
||||||
|
|
||||||
<hr style={{ border: 'none', borderTop: '1px solid var(--border-color)', margin: '0.75rem 0' }} />
|
<hr style={{ border: 'none', borderTop: '1px solid var(--border-color)', margin: '0.75rem 0' }} />
|
||||||
|
|
||||||
{/* Objednávky / Projekty */}
|
{/* Objednávky / Projekty */}
|
||||||
<div className="admin-form-group">
|
<FormField label="Objednávky a projekty — typový kód">
|
||||||
<label className="admin-form-label">Objednávky a projekty — typový kód</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={form.order_type_code}
|
value={form.order_type_code}
|
||||||
@@ -864,13 +848,12 @@ export default function CompanySettings() {
|
|||||||
<small className="admin-form-hint">
|
<small className="admin-form-hint">
|
||||||
Formát: RRKÓD#### — ukázka: {currentYear}{form.order_type_code || '71'}0001
|
Formát: RRKÓD#### — ukázka: {currentYear}{form.order_type_code || '71'}0001
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</FormField>
|
||||||
|
|
||||||
<hr style={{ border: 'none', borderTop: '1px solid var(--border-color)', margin: '0.75rem 0' }} />
|
<hr style={{ border: 'none', borderTop: '1px solid var(--border-color)', margin: '0.75rem 0' }} />
|
||||||
|
|
||||||
{/* Faktury */}
|
{/* Faktury */}
|
||||||
<div className="admin-form-group">
|
<FormField label="Faktury — typový kód">
|
||||||
<label className="admin-form-label">Faktury — typový kód</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={form.invoice_type_code}
|
value={form.invoice_type_code}
|
||||||
@@ -882,7 +865,7 @@ export default function CompanySettings() {
|
|||||||
<small className="admin-form-hint">
|
<small className="admin-form-hint">
|
||||||
Formát: RRKÓD#### — ukázka: {currentYear}{form.invoice_type_code || '81'}0001
|
Formát: RRKÓD#### — ukázka: {currentYear}{form.invoice_type_code || '81'}0001
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
@@ -900,8 +883,7 @@ export default function CompanySettings() {
|
|||||||
<div className="admin-card-body">
|
<div className="admin-card-body">
|
||||||
<div className="admin-form">
|
<div className="admin-form">
|
||||||
<div className="admin-form-row">
|
<div className="admin-form-row">
|
||||||
<div className="admin-form-group">
|
<FormField label="Výchozí měna">
|
||||||
<label className="admin-form-label">Výchozí měna</label>
|
|
||||||
<select
|
<select
|
||||||
value={form.default_currency}
|
value={form.default_currency}
|
||||||
onChange={(e) => updateField('default_currency', e.target.value)}
|
onChange={(e) => updateField('default_currency', e.target.value)}
|
||||||
@@ -912,9 +894,8 @@ export default function CompanySettings() {
|
|||||||
<option value="CZK">CZK (Kč)</option>
|
<option value="CZK">CZK (Kč)</option>
|
||||||
<option value="GBP">GBP (£)</option>
|
<option value="GBP">GBP (£)</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Výchozí sazba DPH (%)">
|
||||||
<label className="admin-form-label">Výchozí sazba DPH (%)</label>
|
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
value={form.default_vat_rate}
|
value={form.default_vat_rate}
|
||||||
@@ -922,7 +903,7 @@ export default function CompanySettings() {
|
|||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
step="0.1"
|
step="0.1"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { useNavigate, useSearchParams, Link } from 'react-router-dom'
|
|||||||
import { useAlert } from '../context/AlertContext'
|
import { useAlert } from '../context/AlertContext'
|
||||||
import { useAuth } from '../context/AuthContext'
|
import { useAuth } from '../context/AuthContext'
|
||||||
import Forbidden from '../components/Forbidden'
|
import Forbidden from '../components/Forbidden'
|
||||||
|
import FormField from '../components/FormField'
|
||||||
import AdminDatePicker from '../components/AdminDatePicker'
|
import AdminDatePicker from '../components/AdminDatePicker'
|
||||||
import { motion } from 'framer-motion'
|
import { motion } from 'framer-motion'
|
||||||
import apiFetch from '../utils/api'
|
import apiFetch from '../utils/api'
|
||||||
@@ -462,17 +463,15 @@ export default function InvoiceCreate() {
|
|||||||
<h3 className="admin-card-title">Základní údaje</h3>
|
<h3 className="admin-card-title">Základní údaje</h3>
|
||||||
<div className="admin-form">
|
<div className="admin-form">
|
||||||
<div className="offers-form-row-3">
|
<div className="offers-form-row-3">
|
||||||
<div className="admin-form-group">
|
<FormField label="Číslo faktury">
|
||||||
<label className="admin-form-label">Číslo faktury</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={invoiceNumber}
|
value={invoiceNumber}
|
||||||
onChange={(e) => setInvoiceNumber(e.target.value)}
|
onChange={(e) => setInvoiceNumber(e.target.value)}
|
||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className={`admin-form-group${errors.customer_id ? ' has-error' : ''}`}>
|
<FormField label="Odběratel" error={errors.customer_id} required>
|
||||||
<label className="admin-form-label required">Odběratel</label>
|
|
||||||
{form.customer_id ? (
|
{form.customer_id ? (
|
||||||
<div className="offers-customer-selected">
|
<div className="offers-customer-selected">
|
||||||
<span>{form.customer_name}</span>
|
<span>{form.customer_name}</span>
|
||||||
@@ -526,10 +525,8 @@ export default function InvoiceCreate() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{errors.customer_id && <span className="admin-form-error">{errors.customer_id}</span>}
|
</FormField>
|
||||||
</div>
|
<FormField label="Vystavil">
|
||||||
<div className="admin-form-group">
|
|
||||||
<label className="admin-form-label">Vystavil</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={form.issued_by}
|
value={form.issued_by}
|
||||||
@@ -537,12 +534,11 @@ export default function InvoiceCreate() {
|
|||||||
readOnly
|
readOnly
|
||||||
style={{ backgroundColor: 'var(--bg-secondary)', cursor: 'default' }}
|
style={{ backgroundColor: 'var(--bg-secondary)', cursor: 'default' }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="admin-form-row">
|
<div className="admin-form-row">
|
||||||
<div className={`admin-form-group${errors.issue_date ? ' has-error' : ''}`}>
|
<FormField label="Datum vystavení" error={errors.issue_date} required>
|
||||||
<label className="admin-form-label required">Datum vystavení</label>
|
|
||||||
<AdminDatePicker
|
<AdminDatePicker
|
||||||
mode="date"
|
mode="date"
|
||||||
value={form.issue_date}
|
value={form.issue_date}
|
||||||
@@ -551,10 +547,8 @@ export default function InvoiceCreate() {
|
|||||||
setErrors(prev => ({ ...prev, issue_date: undefined }))
|
setErrors(prev => ({ ...prev, issue_date: undefined }))
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{errors.issue_date && <span className="admin-form-error">{errors.issue_date}</span>}
|
</FormField>
|
||||||
</div>
|
<FormField label="Splatnost (dny)">
|
||||||
<div className="admin-form-group">
|
|
||||||
<label className="admin-form-label">Splatnost (dny)</label>
|
|
||||||
<select
|
<select
|
||||||
value={dueDays}
|
value={dueDays}
|
||||||
onChange={(e) => setDueDays(Number(e.target.value))}
|
onChange={(e) => setDueDays(Number(e.target.value))}
|
||||||
@@ -569,9 +563,8 @@ export default function InvoiceCreate() {
|
|||||||
Splatnost: {new Date(form.due_date).toLocaleDateString('cs-CZ')}
|
Splatnost: {new Date(form.due_date).toLocaleDateString('cs-CZ')}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</FormField>
|
||||||
<div className={`admin-form-group${errors.tax_date ? ' has-error' : ''}`}>
|
<FormField label="DÚZP" error={errors.tax_date} required>
|
||||||
<label className="admin-form-label required">DÚZP</label>
|
|
||||||
<AdminDatePicker
|
<AdminDatePicker
|
||||||
mode="date"
|
mode="date"
|
||||||
value={form.tax_date}
|
value={form.tax_date}
|
||||||
@@ -580,13 +573,11 @@ export default function InvoiceCreate() {
|
|||||||
setErrors(prev => ({ ...prev, tax_date: undefined }))
|
setErrors(prev => ({ ...prev, tax_date: undefined }))
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{errors.tax_date && <span className="admin-form-error">{errors.tax_date}</span>}
|
</FormField>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="offers-form-row-3">
|
<div className="offers-form-row-3">
|
||||||
<div className="admin-form-group">
|
<FormField label="Forma úhrady">
|
||||||
<label className="admin-form-label">Forma úhrady</label>
|
|
||||||
<select
|
<select
|
||||||
value={form.payment_method}
|
value={form.payment_method}
|
||||||
onChange={(e) => setForm(prev => ({ ...prev, payment_method: e.target.value }))}
|
onChange={(e) => setForm(prev => ({ ...prev, payment_method: e.target.value }))}
|
||||||
@@ -596,9 +587,8 @@ export default function InvoiceCreate() {
|
|||||||
<option value="Hotově">Hotově</option>
|
<option value="Hotově">Hotově</option>
|
||||||
<option value="Dobírka">Dobírka</option>
|
<option value="Dobírka">Dobírka</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Měna">
|
||||||
<label className="admin-form-label">Měna</label>
|
|
||||||
<select
|
<select
|
||||||
value={form.currency}
|
value={form.currency}
|
||||||
onChange={(e) => setForm(prev => ({ ...prev, currency: e.target.value }))}
|
onChange={(e) => setForm(prev => ({ ...prev, currency: e.target.value }))}
|
||||||
@@ -608,9 +598,8 @@ export default function InvoiceCreate() {
|
|||||||
<option value="EUR">EUR (€)</option>
|
<option value="EUR">EUR (€)</option>
|
||||||
<option value="USD">USD ($)</option>
|
<option value="USD">USD ($)</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="DPH">
|
||||||
<label className="admin-form-label">DPH</label>
|
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
|
||||||
<label className="admin-form-checkbox" style={{ whiteSpace: 'nowrap' }}>
|
<label className="admin-form-checkbox" style={{ whiteSpace: 'nowrap' }}>
|
||||||
<input
|
<input
|
||||||
@@ -621,11 +610,10 @@ export default function InvoiceCreate() {
|
|||||||
<span>Uplatnit DPH</span>
|
<span>Uplatnit DPH</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={`admin-form-group${errors.bank_account_id ? ' has-error' : ''}`}>
|
<FormField label="Bankovní účet" error={errors.bank_account_id} required>
|
||||||
<label className="admin-form-label required">Bankovní účet</label>
|
|
||||||
<select
|
<select
|
||||||
value={form.bank_account_id}
|
value={form.bank_account_id}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
@@ -641,8 +629,7 @@ export default function InvoiceCreate() {
|
|||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
{errors.bank_account_id && <span className="admin-form-error">{errors.bank_account_id}</span>}
|
</FormField>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { useParams, useNavigate, Link } from 'react-router-dom'
|
|||||||
import { motion, AnimatePresence } from 'framer-motion'
|
import { motion, AnimatePresence } from 'framer-motion'
|
||||||
import ConfirmModal from '../components/ConfirmModal'
|
import ConfirmModal from '../components/ConfirmModal'
|
||||||
import Forbidden from '../components/Forbidden'
|
import Forbidden from '../components/Forbidden'
|
||||||
|
import FormField from '../components/FormField'
|
||||||
|
|
||||||
import apiFetch from '../utils/api'
|
import apiFetch from '../utils/api'
|
||||||
import DOMPurify from 'dompurify'
|
import DOMPurify from 'dompurify'
|
||||||
@@ -376,8 +377,7 @@ export default function InvoiceDetail() {
|
|||||||
<h3 className="admin-card-title">Informace</h3>
|
<h3 className="admin-card-title">Informace</h3>
|
||||||
<div className="admin-form">
|
<div className="admin-form">
|
||||||
<div className="offers-form-row-3" style={{ marginBottom: '0.5rem' }}>
|
<div className="offers-form-row-3" style={{ marginBottom: '0.5rem' }}>
|
||||||
<div className="admin-form-group">
|
<FormField label="Zákazník">
|
||||||
<label className="admin-form-label">Zákazník</label>
|
|
||||||
<div style={{ fontWeight: 500 }}>{invoice.customer_name || '—'}</div>
|
<div style={{ fontWeight: 500 }}>{invoice.customer_name || '—'}</div>
|
||||||
{invoice.customer && (
|
{invoice.customer && (
|
||||||
<div className="text-tertiary" style={{ fontSize: '0.8rem', marginTop: '0.2rem' }}>
|
<div className="text-tertiary" style={{ fontSize: '0.8rem', marginTop: '0.2rem' }}>
|
||||||
@@ -385,9 +385,8 @@ export default function InvoiceDetail() {
|
|||||||
{invoice.customer.vat_id && ` · DIČ: ${invoice.customer.vat_id}`}
|
{invoice.customer.vat_id && ` · DIČ: ${invoice.customer.vat_id}`}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Objednávka">
|
||||||
<label className="admin-form-label">Objednávka</label>
|
|
||||||
<div>
|
<div>
|
||||||
{invoice.order_id ? (
|
{invoice.order_id ? (
|
||||||
<Link to={`/orders/${invoice.order_id}`} className="link-accent">
|
<Link to={`/orders/${invoice.order_id}`} className="link-accent">
|
||||||
@@ -395,48 +394,40 @@ export default function InvoiceDetail() {
|
|||||||
</Link>
|
</Link>
|
||||||
) : '—'}
|
) : '—'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Měna">
|
||||||
<label className="admin-form-label">Měna</label>
|
|
||||||
<div>{invoice.currency}</div>
|
<div>{invoice.currency}</div>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
<div className="offers-form-row-3" style={{ marginBottom: '0.5rem' }}>
|
<div className="offers-form-row-3" style={{ marginBottom: '0.5rem' }}>
|
||||||
<div className="admin-form-group">
|
<FormField label="Datum vystavení">
|
||||||
<label className="admin-form-label">Datum vystavení</label>
|
|
||||||
<div>{formatDate(invoice.issue_date)}</div>
|
<div>{formatDate(invoice.issue_date)}</div>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Datum splatnosti">
|
||||||
<label className="admin-form-label">Datum splatnosti</label>
|
|
||||||
<div className={invoice.status === 'overdue' ? 'text-danger fw-600' : ''}>
|
<div className={invoice.status === 'overdue' ? 'text-danger fw-600' : ''}>
|
||||||
{formatDate(invoice.due_date)}
|
{formatDate(invoice.due_date)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="DÚZP">
|
||||||
<label className="admin-form-label">DÚZP</label>
|
|
||||||
<div>{formatDate(invoice.tax_date)}</div>
|
<div>{formatDate(invoice.tax_date)}</div>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
<div className="offers-form-row-3">
|
<div className="offers-form-row-3">
|
||||||
<div className="admin-form-group">
|
<FormField label="Forma úhrady">
|
||||||
<label className="admin-form-label">Forma úhrady</label>
|
|
||||||
<div>{invoice.payment_method}</div>
|
<div>{invoice.payment_method}</div>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Variabilní symbol">
|
||||||
<label className="admin-form-label">Variabilní symbol</label>
|
|
||||||
<div>{invoice.invoice_number}</div>
|
<div>{invoice.invoice_number}</div>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Vystavil">
|
||||||
<label className="admin-form-label">Vystavil</label>
|
|
||||||
<div>{invoice.issued_by || '—'}</div>
|
<div>{invoice.issued_by || '—'}</div>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
{invoice.paid_date && (
|
{invoice.paid_date && (
|
||||||
<div className="admin-form-row" style={{ marginTop: '0.5rem' }}>
|
<div className="admin-form-row" style={{ marginTop: '0.5rem' }}>
|
||||||
<div className="admin-form-group">
|
<FormField label="Datum úhrady">
|
||||||
<label className="admin-form-label">Datum úhrady</label>
|
|
||||||
<div style={{ color: 'var(--success)', fontWeight: 500 }}>{formatDate(invoice.paid_date)}</div>
|
<div style={{ color: 'var(--success)', fontWeight: 500 }}>{formatDate(invoice.paid_date)}</div>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { czechPlural } from '../utils/formatters'
|
|||||||
import ConfirmModal from '../components/ConfirmModal'
|
import ConfirmModal from '../components/ConfirmModal'
|
||||||
import Forbidden from '../components/Forbidden'
|
import Forbidden from '../components/Forbidden'
|
||||||
import useModalLock from '../hooks/useModalLock'
|
import useModalLock from '../hooks/useModalLock'
|
||||||
|
import FormField from '../components/FormField'
|
||||||
|
|
||||||
const API_BASE = '/api/admin'
|
const API_BASE = '/api/admin'
|
||||||
|
|
||||||
@@ -417,8 +418,7 @@ export default function LeaveApproval() {
|
|||||||
{formatDate(rejectModal.request.date_from)} — {formatDate(rejectModal.request.date_to)} ({rejectModal.request.total_days} dnů)
|
{formatDate(rejectModal.request.date_from)} — {formatDate(rejectModal.request.date_to)} ({rejectModal.request.total_days} dnů)
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
<div className="admin-form-group">
|
<FormField label="Důvod zamítnutí" required>
|
||||||
<label className="admin-form-label required">Důvod zamítnutí</label>
|
|
||||||
<textarea
|
<textarea
|
||||||
value={rejectNote}
|
value={rejectNote}
|
||||||
onChange={(e) => setRejectNote(e.target.value)}
|
onChange={(e) => setRejectNote(e.target.value)}
|
||||||
@@ -427,7 +427,7 @@ export default function LeaveApproval() {
|
|||||||
rows={3}
|
rows={3}
|
||||||
autoFocus
|
autoFocus
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
<div className="admin-modal-footer">
|
<div className="admin-modal-footer">
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { useAuth } from '../context/AuthContext'
|
|||||||
import { useAlert } from '../context/AlertContext'
|
import { useAlert } from '../context/AlertContext'
|
||||||
import { useTheme } from '../../context/ThemeContext'
|
import { useTheme } from '../../context/ThemeContext'
|
||||||
import { shouldShowSessionExpiredAlert, shouldShowLogoutAlert } from '../utils/api'
|
import { shouldShowSessionExpiredAlert, shouldShowLogoutAlert } from '../utils/api'
|
||||||
|
import FormField from '../components/FormField'
|
||||||
|
|
||||||
export default function Login() {
|
export default function Login() {
|
||||||
const { login, verify2FA, isAuthenticated, loading: authLoading } = useAuth()
|
const { login, verify2FA, isAuthenticated, loading: authLoading } = useAuth()
|
||||||
@@ -166,10 +167,7 @@ export default function Login() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form onSubmit={handleSubmit} className="admin-form">
|
<form onSubmit={handleSubmit} className="admin-form">
|
||||||
<div className="admin-form-group">
|
<FormField label="Uživatelské jméno nebo e-mail">
|
||||||
<label htmlFor="username" className="admin-form-label">
|
|
||||||
Uživatelské jméno nebo e-mail
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
id="username"
|
id="username"
|
||||||
type="text"
|
type="text"
|
||||||
@@ -180,12 +178,9 @@ export default function Login() {
|
|||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
placeholder="Zadejte uživatelské jméno"
|
placeholder="Zadejte uživatelské jméno"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
|
|
||||||
<div className="admin-form-group">
|
<FormField label="Heslo">
|
||||||
<label htmlFor="password" className="admin-form-label">
|
|
||||||
Heslo
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
id="password"
|
id="password"
|
||||||
type="password"
|
type="password"
|
||||||
@@ -196,7 +191,7 @@ export default function Login() {
|
|||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
placeholder="Zadejte heslo"
|
placeholder="Zadejte heslo"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
|
|
||||||
<label className="admin-form-checkbox">
|
<label className="admin-form-checkbox">
|
||||||
<input
|
<input
|
||||||
@@ -256,10 +251,7 @@ export default function Login() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form onSubmit={handle2FASubmit} className="admin-form">
|
<form onSubmit={handle2FASubmit} className="admin-form">
|
||||||
<div className="admin-form-group">
|
<FormField label={useBackupCode ? 'Záložní kód' : 'Ověřovací kód'}>
|
||||||
<label htmlFor="totp-code" className="admin-form-label">
|
|
||||||
{useBackupCode ? 'Záložní kód' : 'Ověřovací kód'}
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
ref={totpInputRef}
|
ref={totpInputRef}
|
||||||
id="totp-code"
|
id="totp-code"
|
||||||
@@ -283,7 +275,7 @@ export default function Login() {
|
|||||||
fontFamily: 'monospace'
|
fontFamily: 'monospace'
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { useParams, useNavigate, Link } from 'react-router-dom'
|
|||||||
import { motion, AnimatePresence } from 'framer-motion'
|
import { motion, AnimatePresence } from 'framer-motion'
|
||||||
|
|
||||||
import ConfirmModal from '../components/ConfirmModal'
|
import ConfirmModal from '../components/ConfirmModal'
|
||||||
|
import FormField from '../components/FormField'
|
||||||
import Forbidden from '../components/Forbidden'
|
import Forbidden from '../components/Forbidden'
|
||||||
import AdminDatePicker from '../components/AdminDatePicker'
|
import AdminDatePicker from '../components/AdminDatePicker'
|
||||||
import OfferItemsSection from '../components/OfferItemsSection'
|
import OfferItemsSection from '../components/OfferItemsSection'
|
||||||
@@ -319,8 +320,7 @@ export default function OfferDetail() {
|
|||||||
<h3 className="admin-card-title">Základní údaje</h3>
|
<h3 className="admin-card-title">Základní údaje</h3>
|
||||||
<div className="admin-form">
|
<div className="admin-form">
|
||||||
<div className="offers-form-row-3">
|
<div className="offers-form-row-3">
|
||||||
<div className="admin-form-group">
|
<FormField label="Číslo nabídky">
|
||||||
<label className="admin-form-label">Číslo nabídky</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={form.quotation_number}
|
value={form.quotation_number}
|
||||||
@@ -328,9 +328,8 @@ export default function OfferDetail() {
|
|||||||
readOnly
|
readOnly
|
||||||
style={{ backgroundColor: 'var(--bg-secondary)', cursor: 'default' }}
|
style={{ backgroundColor: 'var(--bg-secondary)', cursor: 'default' }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Kód projektu">
|
||||||
<label className="admin-form-label">Kód projektu</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={form.project_code}
|
value={form.project_code}
|
||||||
@@ -339,7 +338,7 @@ export default function OfferDetail() {
|
|||||||
placeholder="Volitelný kód projektu"
|
placeholder="Volitelný kód projektu"
|
||||||
readOnly={isInvalidated}
|
readOnly={isInvalidated}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<OfferCustomerPicker
|
<OfferCustomerPicker
|
||||||
customers={customers}
|
customers={customers}
|
||||||
customerId={form.customer_id}
|
customerId={form.customer_id}
|
||||||
@@ -352,8 +351,7 @@ export default function OfferDetail() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="admin-form-row">
|
<div className="admin-form-row">
|
||||||
<div className={`admin-form-group${errors.created_at ? ' has-error' : ''}`}>
|
<FormField label="Datum vytvoření" error={errors.created_at} required>
|
||||||
<label className="admin-form-label required">Datum vytvoření</label>
|
|
||||||
{isInvalidated ? (
|
{isInvalidated ? (
|
||||||
<input type="text" value={form.created_at} className="admin-form-input" readOnly />
|
<input type="text" value={form.created_at} className="admin-form-input" readOnly />
|
||||||
) : (
|
) : (
|
||||||
@@ -366,10 +364,8 @@ export default function OfferDetail() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{errors.created_at && <span className="admin-form-error">{errors.created_at}</span>}
|
</FormField>
|
||||||
</div>
|
<FormField label="Platnost do" error={errors.valid_until} required>
|
||||||
<div className={`admin-form-group${errors.valid_until ? ' has-error' : ''}`}>
|
|
||||||
<label className="admin-form-label required">Platnost do</label>
|
|
||||||
{isInvalidated ? (
|
{isInvalidated ? (
|
||||||
<input type="text" value={form.valid_until} className="admin-form-input" readOnly />
|
<input type="text" value={form.valid_until} className="admin-form-input" readOnly />
|
||||||
) : (
|
) : (
|
||||||
@@ -382,13 +378,11 @@ export default function OfferDetail() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{errors.valid_until && <span className="admin-form-error">{errors.valid_until}</span>}
|
</FormField>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="admin-form-row">
|
<div className="admin-form-row">
|
||||||
<div className="admin-form-group">
|
<FormField label="Měna">
|
||||||
<label className="admin-form-label">Měna</label>
|
|
||||||
<select
|
<select
|
||||||
value={form.currency}
|
value={form.currency}
|
||||||
onChange={(e) => updateForm('currency', e.target.value)}
|
onChange={(e) => updateForm('currency', e.target.value)}
|
||||||
@@ -400,9 +394,8 @@ export default function OfferDetail() {
|
|||||||
<option value="CZK">CZK (Kč)</option>
|
<option value="CZK">CZK (Kč)</option>
|
||||||
<option value="GBP">GBP (£)</option>
|
<option value="GBP">GBP (£)</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Jazyk nabídky">
|
||||||
<label className="admin-form-label">Jazyk nabídky</label>
|
|
||||||
<select
|
<select
|
||||||
value={form.language}
|
value={form.language}
|
||||||
onChange={(e) => updateForm('language', e.target.value)}
|
onChange={(e) => updateForm('language', e.target.value)}
|
||||||
@@ -412,12 +405,11 @@ export default function OfferDetail() {
|
|||||||
<option value="EN">English</option>
|
<option value="EN">English</option>
|
||||||
<option value="CZ">Čeština</option>
|
<option value="CZ">Čeština</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="offers-form-row-3">
|
<div className="offers-form-row-3">
|
||||||
<div className="admin-form-group">
|
<FormField label="Sazba DPH (%)">
|
||||||
<label className="admin-form-label">Sazba DPH (%)</label>
|
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
@@ -438,9 +430,8 @@ export default function OfferDetail() {
|
|||||||
<span>Účtovat DPH</span>
|
<span>Účtovat DPH</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Směnný kurz">
|
||||||
<label className="admin-form-label">Směnný kurz</label>
|
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
value={form.exchange_rate}
|
value={form.exchange_rate}
|
||||||
@@ -450,7 +441,7 @@ export default function OfferDetail() {
|
|||||||
step="0.0001"
|
step="0.0001"
|
||||||
readOnly={isInvalidated}
|
readOnly={isInvalidated}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
@@ -510,8 +501,7 @@ export default function OfferDetail() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="admin-modal-body">
|
<div className="admin-modal-body">
|
||||||
<div className="admin-form">
|
<div className="admin-form">
|
||||||
<div className="admin-form-group">
|
<FormField label="Číslo objednávky zákazníka" required>
|
||||||
<label className="admin-form-label required">Číslo objednávky zákazníka</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={customerOrderNumber}
|
value={customerOrderNumber}
|
||||||
@@ -521,9 +511,8 @@ export default function OfferDetail() {
|
|||||||
placeholder="Např. PO-2026-001"
|
placeholder="Např. PO-2026-001"
|
||||||
autoFocus
|
autoFocus
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Příloha (PDF)">
|
||||||
<label className="admin-form-label">Příloha (PDF)</label>
|
|
||||||
{orderAttachment ? (
|
{orderAttachment ? (
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--accent-color)" strokeWidth="2">
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--accent-color)" strokeWidth="2">
|
||||||
@@ -562,7 +551,7 @@ export default function OfferDetail() {
|
|||||||
</label>
|
</label>
|
||||||
)}
|
)}
|
||||||
<small className="admin-form-hint" style={{ marginTop: '0.25rem' }}>Max 10 MB</small>
|
<small className="admin-form-hint" style={{ marginTop: '0.25rem' }}>Max 10 MB</small>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="admin-modal-footer">
|
<div className="admin-modal-footer">
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import useTableSort from '../hooks/useTableSort'
|
|||||||
import useListData from '../hooks/useListData'
|
import useListData from '../hooks/useListData'
|
||||||
import useModalLock from '../hooks/useModalLock'
|
import useModalLock from '../hooks/useModalLock'
|
||||||
import Pagination from '../components/Pagination'
|
import Pagination from '../components/Pagination'
|
||||||
|
import FormField from '../components/FormField'
|
||||||
const API_BASE = '/api/admin'
|
const API_BASE = '/api/admin'
|
||||||
const DRAFT_KEY = 'boha_offer_draft'
|
const DRAFT_KEY = 'boha_offer_draft'
|
||||||
|
|
||||||
@@ -564,8 +565,7 @@ export default function Offers() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="admin-modal-body">
|
<div className="admin-modal-body">
|
||||||
<div className="admin-form">
|
<div className="admin-form">
|
||||||
<div className="admin-form-group">
|
<FormField label="Číslo objednávky zákazníka" required>
|
||||||
<label className="admin-form-label required">Číslo objednávky zákazníka</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={customerOrderNumber}
|
value={customerOrderNumber}
|
||||||
@@ -575,9 +575,8 @@ export default function Offers() {
|
|||||||
placeholder="Např. PO-2026-001"
|
placeholder="Např. PO-2026-001"
|
||||||
autoFocus
|
autoFocus
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Příloha (PDF)">
|
||||||
<label className="admin-form-label">Příloha (PDF)</label>
|
|
||||||
{orderAttachment ? (
|
{orderAttachment ? (
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--accent-color)" strokeWidth="2">
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--accent-color)" strokeWidth="2">
|
||||||
@@ -616,7 +615,7 @@ export default function Offers() {
|
|||||||
</label>
|
</label>
|
||||||
)}
|
)}
|
||||||
<small className="admin-form-hint" style={{ marginTop: '0.25rem' }}>Max 10 MB</small>
|
<small className="admin-form-hint" style={{ marginTop: '0.25rem' }}>Max 10 MB</small>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="admin-modal-footer">
|
<div className="admin-modal-footer">
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { useAlert } from '../context/AlertContext'
|
|||||||
import { useAuth } from '../context/AuthContext'
|
import { useAuth } from '../context/AuthContext'
|
||||||
import { motion, AnimatePresence } from 'framer-motion'
|
import { motion, AnimatePresence } from 'framer-motion'
|
||||||
import ConfirmModal from '../components/ConfirmModal'
|
import ConfirmModal from '../components/ConfirmModal'
|
||||||
|
import FormField from '../components/FormField'
|
||||||
import Forbidden from '../components/Forbidden'
|
import Forbidden from '../components/Forbidden'
|
||||||
import useModalLock from '../hooks/useModalLock'
|
import useModalLock from '../hooks/useModalLock'
|
||||||
|
|
||||||
@@ -408,8 +409,7 @@ export default function OffersCustomers() {
|
|||||||
|
|
||||||
<div className="admin-modal-body">
|
<div className="admin-modal-body">
|
||||||
<div className="admin-form">
|
<div className="admin-form">
|
||||||
<div className="admin-form-group">
|
<FormField label="Název" required>
|
||||||
<label className="admin-form-label required">Název</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={form.name}
|
value={form.name}
|
||||||
@@ -417,64 +417,58 @@ export default function OffersCustomers() {
|
|||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
placeholder="Název firmy / jméno"
|
placeholder="Název firmy / jméno"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Ulice">
|
||||||
<label className="admin-form-label">Ulice</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={form.street}
|
value={form.street}
|
||||||
onChange={(e) => setForm(prev => ({ ...prev, street: e.target.value }))}
|
onChange={(e) => setForm(prev => ({ ...prev, street: e.target.value }))}
|
||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-row">
|
<div className="admin-form-row">
|
||||||
<div className="admin-form-group">
|
<FormField label="Město">
|
||||||
<label className="admin-form-label">Město</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={form.city}
|
value={form.city}
|
||||||
onChange={(e) => setForm(prev => ({ ...prev, city: e.target.value }))}
|
onChange={(e) => setForm(prev => ({ ...prev, city: e.target.value }))}
|
||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="PSČ">
|
||||||
<label className="admin-form-label">PSČ</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={form.postal_code}
|
value={form.postal_code}
|
||||||
onChange={(e) => setForm(prev => ({ ...prev, postal_code: e.target.value }))}
|
onChange={(e) => setForm(prev => ({ ...prev, postal_code: e.target.value }))}
|
||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
/>
|
/>
|
||||||
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<FormField label="Země">
|
||||||
<div className="admin-form-group">
|
|
||||||
<label className="admin-form-label">Země</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={form.country}
|
value={form.country}
|
||||||
onChange={(e) => setForm(prev => ({ ...prev, country: e.target.value }))}
|
onChange={(e) => setForm(prev => ({ ...prev, country: e.target.value }))}
|
||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-row">
|
<div className="admin-form-row">
|
||||||
<div className="admin-form-group">
|
<FormField label="IČO">
|
||||||
<label className="admin-form-label">IČO</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={form.company_id}
|
value={form.company_id}
|
||||||
onChange={(e) => setForm(prev => ({ ...prev, company_id: e.target.value }))}
|
onChange={(e) => setForm(prev => ({ ...prev, company_id: e.target.value }))}
|
||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="DIČ">
|
||||||
<label className="admin-form-label">DIČ</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={form.vat_id}
|
value={form.vat_id}
|
||||||
onChange={(e) => setForm(prev => ({ ...prev, vat_id: e.target.value }))}
|
onChange={(e) => setForm(prev => ({ ...prev, vat_id: e.target.value }))}
|
||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Dynamic custom fields */}
|
{/* Dynamic custom fields */}
|
||||||
@@ -483,8 +477,7 @@ export default function OffersCustomers() {
|
|||||||
{customFields.map((field, idx) => (
|
{customFields.map((field, idx) => (
|
||||||
<div key={field._key} style={{ marginBottom: 8 }}>
|
<div key={field._key} style={{ marginBottom: 8 }}>
|
||||||
<div className="admin-form-row" style={{ marginBottom: 0, alignItems: 'flex-end' }}>
|
<div className="admin-form-row" style={{ marginBottom: 0, alignItems: 'flex-end' }}>
|
||||||
<div className="admin-form-group" style={{ flex: 1 }}>
|
<FormField label={idx === 0 ? 'Název' : '\u00A0'} style={{ flex: 1 }}>
|
||||||
{idx === 0 && <label className="admin-form-label" style={{ fontSize: '0.75rem' }}>Název</label>}
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={field.name}
|
value={field.name}
|
||||||
@@ -496,9 +489,8 @@ export default function OffersCustomers() {
|
|||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
placeholder="Např. Kontakt"
|
placeholder="Např. Kontakt"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group" style={{ flex: 1 }}>
|
<FormField label={idx === 0 ? 'Hodnota' : '\u00A0'} style={{ flex: 1 }}>
|
||||||
{idx === 0 && <label className="admin-form-label" style={{ fontSize: '0.75rem' }}>Hodnota</label>}
|
|
||||||
<div style={{ display: 'flex', gap: 4, alignItems: 'center' }}>
|
<div style={{ display: 'flex', gap: 4, alignItems: 'center' }}>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
@@ -537,7 +529,7 @@ export default function OffersCustomers() {
|
|||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
<label className="admin-form-checkbox" style={{ marginTop: 4 }}>
|
<label className="admin-form-checkbox" style={{ marginTop: 4 }}>
|
||||||
<input
|
<input
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { useAlert } from '../context/AlertContext'
|
|||||||
import { useAuth } from '../context/AuthContext'
|
import { useAuth } from '../context/AuthContext'
|
||||||
import { motion, AnimatePresence } from 'framer-motion'
|
import { motion, AnimatePresence } from 'framer-motion'
|
||||||
import ConfirmModal from '../components/ConfirmModal'
|
import ConfirmModal from '../components/ConfirmModal'
|
||||||
|
import FormField from '../components/FormField'
|
||||||
import Forbidden from '../components/Forbidden'
|
import Forbidden from '../components/Forbidden'
|
||||||
import RichEditor from '../components/RichEditor'
|
import RichEditor from '../components/RichEditor'
|
||||||
import useModalLock from '../hooks/useModalLock'
|
import useModalLock from '../hooks/useModalLock'
|
||||||
@@ -238,23 +239,19 @@ function ItemTemplatesTab() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="admin-modal-body">
|
<div className="admin-modal-body">
|
||||||
<div className="admin-form">
|
<div className="admin-form">
|
||||||
<div className="admin-form-group">
|
<FormField label="Název" required>
|
||||||
<label className="admin-form-label required">Název</label>
|
|
||||||
<input type="text" value={form.name} onChange={(e) => setForm(p => ({ ...p, name: e.target.value }))} className="admin-form-input" />
|
<input type="text" value={form.name} onChange={(e) => setForm(p => ({ ...p, name: e.target.value }))} className="admin-form-input" />
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Popis">
|
||||||
<label className="admin-form-label">Popis</label>
|
|
||||||
<textarea value={form.description} onChange={(e) => setForm(p => ({ ...p, description: e.target.value }))} className="admin-form-input" rows={2} />
|
<textarea value={form.description} onChange={(e) => setForm(p => ({ ...p, description: e.target.value }))} className="admin-form-input" rows={2} />
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-row">
|
<div className="admin-form-row">
|
||||||
<div className="admin-form-group">
|
<FormField label="Výchozí cena">
|
||||||
<label className="admin-form-label">Výchozí cena</label>
|
|
||||||
<input type="number" value={form.default_price} onChange={(e) => setForm(p => ({ ...p, default_price: parseFloat(e.target.value) || 0 }))} className="admin-form-input" step="0.01" />
|
<input type="number" value={form.default_price} onChange={(e) => setForm(p => ({ ...p, default_price: parseFloat(e.target.value) || 0 }))} className="admin-form-input" step="0.01" />
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Kategorie">
|
||||||
<label className="admin-form-label">Kategorie</label>
|
|
||||||
<input type="text" value={form.category} onChange={(e) => setForm(p => ({ ...p, category: e.target.value }))} className="admin-form-input" />
|
<input type="text" value={form.category} onChange={(e) => setForm(p => ({ ...p, category: e.target.value }))} className="admin-form-input" />
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -509,10 +506,9 @@ function ScopeTemplatesTab() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="admin-modal-body">
|
<div className="admin-modal-body">
|
||||||
<div className="admin-form">
|
<div className="admin-form">
|
||||||
<div className="admin-form-group">
|
<FormField label="Název šablony" required>
|
||||||
<label className="admin-form-label required">Název šablony</label>
|
|
||||||
<input type="text" value={form.name} onChange={(e) => setForm(p => ({ ...p, name: e.target.value }))} className="admin-form-input" />
|
<input type="text" value={form.name} onChange={(e) => setForm(p => ({ ...p, name: e.target.value }))} className="admin-form-input" />
|
||||||
</div>
|
</FormField>
|
||||||
|
|
||||||
<div className="admin-form-group">
|
<div className="admin-form-group">
|
||||||
<label className="admin-form-label" style={{ marginBottom: '0.5rem' }}>Sekce</label>
|
<label className="admin-form-label" style={{ marginBottom: '0.5rem' }}>Sekce</label>
|
||||||
@@ -538,30 +534,21 @@ function ScopeTemplatesTab() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="admin-form">
|
<div className="admin-form">
|
||||||
<div className="admin-form-row">
|
<div className="admin-form-row">
|
||||||
<div className="admin-form-group">
|
<FormField label={<><span className="offers-lang-badge">EN</span> Název sekce</>}>
|
||||||
<label className="admin-form-label">
|
|
||||||
<span className="offers-lang-badge">EN</span>
|
|
||||||
Název sekce
|
|
||||||
</label>
|
|
||||||
<input type="text" value={section.title} onChange={(e) => updateSection(index, 'title', e.target.value)} className="admin-form-input" placeholder="Název sekce (anglicky)" />
|
<input type="text" value={section.title} onChange={(e) => updateSection(index, 'title', e.target.value)} className="admin-form-input" placeholder="Název sekce (anglicky)" />
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label={<><span className="offers-lang-badge offers-lang-badge-cz">CZ</span> Název sekce</>}>
|
||||||
<label className="admin-form-label">
|
|
||||||
<span className="offers-lang-badge offers-lang-badge-cz">CZ</span>
|
|
||||||
Název sekce
|
|
||||||
</label>
|
|
||||||
<input type="text" value={section.title_cz} onChange={(e) => updateSection(index, 'title_cz', e.target.value)} className="admin-form-input" placeholder="Název sekce (česky)" />
|
<input type="text" value={section.title_cz} onChange={(e) => updateSection(index, 'title_cz', e.target.value)} className="admin-form-input" placeholder="Název sekce (česky)" />
|
||||||
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<FormField label="Obsah">
|
||||||
<div className="admin-form-group">
|
|
||||||
<label className="admin-form-label">Obsah</label>
|
|
||||||
<RichEditor
|
<RichEditor
|
||||||
value={section.content}
|
value={section.content}
|
||||||
onChange={(val) => updateSection(index, 'content', val)}
|
onChange={(val) => updateSection(index, 'content', val)}
|
||||||
placeholder="Obsah sekce..."
|
placeholder="Obsah sekce..."
|
||||||
minHeight="150px"
|
minHeight="150px"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { useAuth } from '../context/AuthContext'
|
|||||||
import { useParams, useNavigate, Link } from 'react-router-dom'
|
import { useParams, useNavigate, Link } from 'react-router-dom'
|
||||||
import { motion } from 'framer-motion'
|
import { motion } from 'framer-motion'
|
||||||
import ConfirmModal from '../components/ConfirmModal'
|
import ConfirmModal from '../components/ConfirmModal'
|
||||||
|
import FormField from '../components/FormField'
|
||||||
import Forbidden from '../components/Forbidden'
|
import Forbidden from '../components/Forbidden'
|
||||||
|
|
||||||
import apiFetch from '../utils/api'
|
import apiFetch from '../utils/api'
|
||||||
@@ -372,8 +373,7 @@ export default function OrderDetail() {
|
|||||||
<div className="admin-card-body">
|
<div className="admin-card-body">
|
||||||
<h3 className="admin-card-title">Informace</h3>
|
<h3 className="admin-card-title">Informace</h3>
|
||||||
<div className="admin-form-row" style={{ marginBottom: '0.5rem' }}>
|
<div className="admin-form-row" style={{ marginBottom: '0.5rem' }}>
|
||||||
<div className="admin-form-group">
|
<FormField label="Nabídka">
|
||||||
<label className="admin-form-label">Nabídka</label>
|
|
||||||
<div>
|
<div>
|
||||||
<Link to={`/offers/${order.quotation_id}`} className="link-accent">
|
<Link to={`/offers/${order.quotation_id}`} className="link-accent">
|
||||||
{order.quotation_number}
|
{order.quotation_number}
|
||||||
@@ -382,9 +382,8 @@ export default function OrderDetail() {
|
|||||||
<span className="text-tertiary" style={{ marginLeft: '0.5rem' }}>({order.project_code})</span>
|
<span className="text-tertiary" style={{ marginLeft: '0.5rem' }}>({order.project_code})</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Projekt">
|
||||||
<label className="admin-form-label">Projekt</label>
|
|
||||||
<div>
|
<div>
|
||||||
{order.project ? (
|
{order.project ? (
|
||||||
<Link to={`/projects/${order.project.id}`} className="link-accent">
|
<Link to={`/projects/${order.project.id}`} className="link-accent">
|
||||||
@@ -392,29 +391,24 @@ export default function OrderDetail() {
|
|||||||
</Link>
|
</Link>
|
||||||
) : '—'}
|
) : '—'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
<div className="admin-form-row admin-form-row-3" style={{ marginBottom: '0.5rem' }}>
|
<div className="admin-form-row admin-form-row-3" style={{ marginBottom: '0.5rem' }}>
|
||||||
<div className="admin-form-group">
|
<FormField label="Zákazník">
|
||||||
<label className="admin-form-label">Zákazník</label>
|
|
||||||
<div style={{ fontWeight: 500 }}>{order.customer_name || '—'}</div>
|
<div style={{ fontWeight: 500 }}>{order.customer_name || '—'}</div>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Číslo obj. zákazníka">
|
||||||
<label className="admin-form-label">Číslo obj. zákazníka</label>
|
|
||||||
<div>{order.customer_order_number || '—'}</div>
|
<div>{order.customer_order_number || '—'}</div>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Měna">
|
||||||
<label className="admin-form-label">Měna</label>
|
|
||||||
<div>{order.currency}</div>
|
<div>{order.currency}</div>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
<div className="admin-form-row admin-form-row-3" style={{ marginBottom: '0.5rem' }}>
|
<div className="admin-form-row admin-form-row-3" style={{ marginBottom: '0.5rem' }}>
|
||||||
<div className="admin-form-group">
|
<FormField label="Datum vytvoření">
|
||||||
<label className="admin-form-label">Datum vytvoření</label>
|
|
||||||
<div>{formatDate(order.created_at)}</div>
|
<div>{formatDate(order.created_at)}</div>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Příloha">
|
||||||
<label className="admin-form-label">Příloha</label>
|
|
||||||
<div>
|
<div>
|
||||||
{order.attachment_name ? (
|
{order.attachment_name ? (
|
||||||
<button
|
<button
|
||||||
@@ -435,7 +429,7 @@ export default function OrderDetail() {
|
|||||||
</button>
|
</button>
|
||||||
) : '—'}
|
) : '—'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
@@ -556,7 +550,7 @@ export default function OrderDetail() {
|
|||||||
>
|
>
|
||||||
<div className="admin-card-body">
|
<div className="admin-card-body">
|
||||||
<h3 className="admin-card-title">Poznámky</h3>
|
<h3 className="admin-card-title">Poznámky</h3>
|
||||||
<div className="admin-form-group">
|
<FormField label="Poznámky">
|
||||||
<textarea
|
<textarea
|
||||||
value={notes}
|
value={notes}
|
||||||
onChange={(e) => setNotes(e.target.value)}
|
onChange={(e) => setNotes(e.target.value)}
|
||||||
@@ -565,7 +559,7 @@ export default function OrderDetail() {
|
|||||||
placeholder="Interní poznámky k objednávce..."
|
placeholder="Interní poznámky k objednávce..."
|
||||||
disabled={!hasPermission('orders.edit')}
|
disabled={!hasPermission('orders.edit')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
{hasPermission('orders.edit') && (
|
{hasPermission('orders.edit') && (
|
||||||
<div style={{ marginTop: '0.5rem' }}>
|
<div style={{ marginTop: '0.5rem' }}>
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { useNavigate, Link } from 'react-router-dom'
|
|||||||
import { useAlert } from '../context/AlertContext'
|
import { useAlert } from '../context/AlertContext'
|
||||||
import { useAuth } from '../context/AuthContext'
|
import { useAuth } from '../context/AuthContext'
|
||||||
import { motion } from 'framer-motion'
|
import { motion } from 'framer-motion'
|
||||||
|
import FormField from '../components/FormField'
|
||||||
import Forbidden from '../components/Forbidden'
|
import Forbidden from '../components/Forbidden'
|
||||||
import AdminDatePicker from '../components/AdminDatePicker'
|
import AdminDatePicker from '../components/AdminDatePicker'
|
||||||
import apiFetch from '../utils/api'
|
import apiFetch from '../utils/api'
|
||||||
@@ -190,8 +191,7 @@ export default function ProjectCreate() {
|
|||||||
<h3 className="admin-card-title">Základní údaje</h3>
|
<h3 className="admin-card-title">Základní údaje</h3>
|
||||||
<div className="admin-form">
|
<div className="admin-form">
|
||||||
<div className="admin-form-row">
|
<div className="admin-form-row">
|
||||||
<div className="admin-form-group">
|
<FormField label="Číslo projektu">
|
||||||
<label className="admin-form-label">Číslo projektu</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={form.project_number}
|
value={form.project_number}
|
||||||
@@ -199,9 +199,8 @@ export default function ProjectCreate() {
|
|||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
placeholder="Ponechte prázdné pro automatické"
|
placeholder="Ponechte prázdné pro automatické"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className={`admin-form-group${errors.name ? ' has-error' : ''}`}>
|
<FormField label="Název" error={errors.name} required>
|
||||||
<label className="admin-form-label required">Název</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={form.name}
|
value={form.name}
|
||||||
@@ -209,13 +208,11 @@ export default function ProjectCreate() {
|
|||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
placeholder="Název projektu"
|
placeholder="Název projektu"
|
||||||
/>
|
/>
|
||||||
{errors.name && <span className="admin-form-error">{errors.name}</span>}
|
</FormField>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="admin-form-row">
|
<div className="admin-form-row">
|
||||||
<div className={`admin-form-group${errors.customer_id ? ' has-error' : ''}`}>
|
<FormField label="Zákazník" error={errors.customer_id} required>
|
||||||
<label className="admin-form-label required">Zákazník</label>
|
|
||||||
{form.customer_id ? (
|
{form.customer_id ? (
|
||||||
<div className="offers-customer-selected">
|
<div className="offers-customer-selected">
|
||||||
<span>{form.customer_name}</span>
|
<span>{form.customer_name}</span>
|
||||||
@@ -257,16 +254,14 @@ export default function ProjectCreate() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{errors.customer_id && <span className="admin-form-error">{errors.customer_id}</span>}
|
</FormField>
|
||||||
</div>
|
<FormField label="Datum zahájení">
|
||||||
<div className="admin-form-group">
|
|
||||||
<label className="admin-form-label">Datum zahájení</label>
|
|
||||||
<AdminDatePicker
|
<AdminDatePicker
|
||||||
mode="date"
|
mode="date"
|
||||||
value={form.start_date}
|
value={form.start_date}
|
||||||
onChange={(val) => updateForm('start_date', val)}
|
onChange={(val) => updateForm('start_date', val)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { motion } from 'framer-motion'
|
|||||||
|
|
||||||
import Forbidden from '../components/Forbidden'
|
import Forbidden from '../components/Forbidden'
|
||||||
import ConfirmModal from '../components/ConfirmModal'
|
import ConfirmModal from '../components/ConfirmModal'
|
||||||
|
import FormField from '../components/FormField'
|
||||||
import AdminDatePicker from '../components/AdminDatePicker'
|
import AdminDatePicker from '../components/AdminDatePicker'
|
||||||
import apiFetch from '../utils/api'
|
import apiFetch from '../utils/api'
|
||||||
const API_BASE = '/api/admin'
|
const API_BASE = '/api/admin'
|
||||||
@@ -300,8 +301,7 @@ export default function ProjectDetail() {
|
|||||||
<h3 className="admin-card-title">Základní údaje</h3>
|
<h3 className="admin-card-title">Základní údaje</h3>
|
||||||
<div className="admin-form">
|
<div className="admin-form">
|
||||||
<div className="admin-form-row">
|
<div className="admin-form-row">
|
||||||
<div className="admin-form-group">
|
<FormField label="Číslo projektu">
|
||||||
<label className="admin-form-label">Číslo projektu</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={project.project_number}
|
value={project.project_number}
|
||||||
@@ -309,9 +309,8 @@ export default function ProjectDetail() {
|
|||||||
readOnly
|
readOnly
|
||||||
style={{ backgroundColor: 'var(--bg-secondary)', cursor: 'default' }}
|
style={{ backgroundColor: 'var(--bg-secondary)', cursor: 'default' }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Název">
|
||||||
<label className="admin-form-label">Název</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={form.name}
|
value={form.name}
|
||||||
@@ -320,12 +319,11 @@ export default function ProjectDetail() {
|
|||||||
placeholder="Název projektu"
|
placeholder="Název projektu"
|
||||||
disabled={!canEdit}
|
disabled={!canEdit}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="admin-form-row">
|
<div className="admin-form-row">
|
||||||
<div className="admin-form-group">
|
<FormField label="Zákazník">
|
||||||
<label className="admin-form-label">Zákazník</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={project.customer_name || '—'}
|
value={project.customer_name || '—'}
|
||||||
@@ -333,9 +331,8 @@ export default function ProjectDetail() {
|
|||||||
readOnly
|
readOnly
|
||||||
style={{ backgroundColor: 'var(--bg-secondary)', cursor: 'default' }}
|
style={{ backgroundColor: 'var(--bg-secondary)', cursor: 'default' }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Stav">
|
||||||
<label className="admin-form-label">Stav</label>
|
|
||||||
<select
|
<select
|
||||||
value={form.status}
|
value={form.status}
|
||||||
onChange={(e) => updateForm('status', e.target.value)}
|
onChange={(e) => updateForm('status', e.target.value)}
|
||||||
@@ -346,28 +343,26 @@ export default function ProjectDetail() {
|
|||||||
<option value="dokonceny">Dokončený</option>
|
<option value="dokonceny">Dokončený</option>
|
||||||
<option value="zruseny">Zrušený</option>
|
<option value="zruseny">Zrušený</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="admin-form-row">
|
<div className="admin-form-row">
|
||||||
<div className="admin-form-group">
|
<FormField label="Datum zahájení">
|
||||||
<label className="admin-form-label">Datum zahájení</label>
|
|
||||||
<AdminDatePicker
|
<AdminDatePicker
|
||||||
mode="date"
|
mode="date"
|
||||||
value={form.start_date}
|
value={form.start_date}
|
||||||
onChange={(val) => updateForm('start_date', val)}
|
onChange={(val) => updateForm('start_date', val)}
|
||||||
disabled={!canEdit}
|
disabled={!canEdit}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Datum ukončení">
|
||||||
<label className="admin-form-label">Datum ukončení</label>
|
|
||||||
<AdminDatePicker
|
<AdminDatePicker
|
||||||
mode="date"
|
mode="date"
|
||||||
value={form.end_date}
|
value={form.end_date}
|
||||||
onChange={(val) => updateForm('end_date', val)}
|
onChange={(val) => updateForm('end_date', val)}
|
||||||
disabled={!canEdit}
|
disabled={!canEdit}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -505,8 +500,7 @@ export default function ProjectDetail() {
|
|||||||
<div className="admin-card-body">
|
<div className="admin-card-body">
|
||||||
<h3 className="admin-card-title">Propojení</h3>
|
<h3 className="admin-card-title">Propojení</h3>
|
||||||
<div className="admin-form-row">
|
<div className="admin-form-row">
|
||||||
<div className="admin-form-group">
|
<FormField label="Objednávka">
|
||||||
<label className="admin-form-label">Objednávka</label>
|
|
||||||
<div>
|
<div>
|
||||||
{project.order_id ? (
|
{project.order_id ? (
|
||||||
<Link to={`/orders/${project.order_id}`} className="link-accent">
|
<Link to={`/orders/${project.order_id}`} className="link-accent">
|
||||||
@@ -519,9 +513,8 @@ export default function ProjectDetail() {
|
|||||||
</Link>
|
</Link>
|
||||||
) : '—'}
|
) : '—'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Nabídka">
|
||||||
<label className="admin-form-label">Nabídka</label>
|
|
||||||
<div>
|
<div>
|
||||||
{project.quotation_id ? (
|
{project.quotation_id ? (
|
||||||
<Link to={`/offers/${project.quotation_id}`} className="link-accent">
|
<Link to={`/offers/${project.quotation_id}`} className="link-accent">
|
||||||
@@ -529,7 +522,7 @@ export default function ProjectDetail() {
|
|||||||
</Link>
|
</Link>
|
||||||
) : '—'}
|
) : '—'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { useAlert } from '../context/AlertContext'
|
|||||||
import { useAuth } from '../context/AuthContext'
|
import { useAuth } from '../context/AuthContext'
|
||||||
import { motion, AnimatePresence } from 'framer-motion'
|
import { motion, AnimatePresence } from 'framer-motion'
|
||||||
import ConfirmModal from '../components/ConfirmModal'
|
import ConfirmModal from '../components/ConfirmModal'
|
||||||
|
import FormField from '../components/FormField'
|
||||||
import apiFetch from '../utils/api'
|
import apiFetch from '../utils/api'
|
||||||
import { formatCurrency, formatDate, czechPlural } from '../utils/formatters'
|
import { formatCurrency, formatDate, czechPlural } from '../utils/formatters'
|
||||||
import SortIcon from '../components/SortIcon'
|
import SortIcon from '../components/SortIcon'
|
||||||
@@ -664,30 +665,24 @@ export default function ReceivedInvoicesProps({ statsMonth, statsYear, uploadOpe
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="received-upload-card-fields">
|
<div className="received-upload-card-fields">
|
||||||
<div className="admin-form-group">
|
<FormField label="Dodavatel" error={uploadErrors[idx]?.supplier_name} required>
|
||||||
<label className="admin-form-label required">Dodavatel</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className={`admin-form-input${uploadErrors[idx]?.supplier_name ? ' has-error' : ''}`}
|
className={`admin-form-input${uploadErrors[idx]?.supplier_name ? ' has-error' : ''}`}
|
||||||
value={uploadMeta[idx]?.supplier_name || ''}
|
value={uploadMeta[idx]?.supplier_name || ''}
|
||||||
onChange={(e) => updateMeta(idx, 'supplier_name', e.target.value)}
|
onChange={(e) => updateMeta(idx, 'supplier_name', e.target.value)}
|
||||||
/>
|
/>
|
||||||
{uploadErrors[idx]?.supplier_name && (
|
</FormField>
|
||||||
<div className="admin-form-error">{uploadErrors[idx].supplier_name}</div>
|
<FormField label="Č. faktury">
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className="admin-form-group">
|
|
||||||
<label className="admin-form-label">Č. faktury</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
value={uploadMeta[idx]?.invoice_number || ''}
|
value={uploadMeta[idx]?.invoice_number || ''}
|
||||||
onChange={(e) => updateMeta(idx, 'invoice_number', e.target.value)}
|
onChange={(e) => updateMeta(idx, 'invoice_number', e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="received-upload-row">
|
<div className="received-upload-row">
|
||||||
<div className="admin-form-group" style={{ flex: 1 }}>
|
<FormField label="Částka" error={uploadErrors[idx]?.amount} required style={{ flex: 1 }}>
|
||||||
<label className="admin-form-label required">Částka</label>
|
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
step="0.01"
|
step="0.01"
|
||||||
@@ -696,12 +691,8 @@ export default function ReceivedInvoicesProps({ statsMonth, statsYear, uploadOpe
|
|||||||
value={uploadMeta[idx]?.amount || ''}
|
value={uploadMeta[idx]?.amount || ''}
|
||||||
onChange={(e) => updateMeta(idx, 'amount', e.target.value)}
|
onChange={(e) => updateMeta(idx, 'amount', e.target.value)}
|
||||||
/>
|
/>
|
||||||
{uploadErrors[idx]?.amount && (
|
</FormField>
|
||||||
<div className="admin-form-error">{uploadErrors[idx].amount}</div>
|
<FormField label="Měna" style={{ width: '90px' }}>
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className="admin-form-group" style={{ width: '90px' }}>
|
|
||||||
<label className="admin-form-label">Měna</label>
|
|
||||||
<select
|
<select
|
||||||
className="admin-form-select"
|
className="admin-form-select"
|
||||||
value={uploadMeta[idx]?.currency || 'CZK'}
|
value={uploadMeta[idx]?.currency || 'CZK'}
|
||||||
@@ -709,9 +700,8 @@ export default function ReceivedInvoicesProps({ statsMonth, statsYear, uploadOpe
|
|||||||
>
|
>
|
||||||
{CURRENCY_OPTIONS.map(c => <option key={c} value={c}>{c}</option>)}
|
{CURRENCY_OPTIONS.map(c => <option key={c} value={c}>{c}</option>)}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group" style={{ width: '90px' }}>
|
<FormField label="DPH %" style={{ width: '90px' }}>
|
||||||
<label className="admin-form-label">DPH %</label>
|
|
||||||
<select
|
<select
|
||||||
className="admin-form-select"
|
className="admin-form-select"
|
||||||
value={uploadMeta[idx]?.vat_rate || '21'}
|
value={uploadMeta[idx]?.vat_rate || '21'}
|
||||||
@@ -719,7 +709,7 @@ export default function ReceivedInvoicesProps({ statsMonth, statsYear, uploadOpe
|
|||||||
>
|
>
|
||||||
{VAT_RATE_OPTIONS.map(r => <option key={r} value={String(r)}>{r}%</option>)}
|
{VAT_RATE_OPTIONS.map(r => <option key={r} value={String(r)}>{r}%</option>)}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
{uploadMeta[idx]?.amount && (
|
{uploadMeta[idx]?.amount && (
|
||||||
<div style={{ fontSize: '0.75rem', color: 'var(--text-tertiary)', marginTop: '-0.25rem', marginBottom: '0.5rem' }}>
|
<div style={{ fontSize: '0.75rem', color: 'var(--text-tertiary)', marginTop: '-0.25rem', marginBottom: '0.5rem' }}>
|
||||||
@@ -730,32 +720,29 @@ export default function ReceivedInvoicesProps({ statsMonth, statsYear, uploadOpe
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="received-upload-row">
|
<div className="received-upload-row">
|
||||||
<div className="admin-form-group" style={{ flex: 1 }}>
|
<FormField label="Datum vystavení" style={{ flex: 1 }}>
|
||||||
<label className="admin-form-label">Datum vystavení</label>
|
|
||||||
<AdminDatePicker
|
<AdminDatePicker
|
||||||
mode="date"
|
mode="date"
|
||||||
value={uploadMeta[idx]?.issue_date || ''}
|
value={uploadMeta[idx]?.issue_date || ''}
|
||||||
onChange={(val) => updateMeta(idx, 'issue_date', val)}
|
onChange={(val) => updateMeta(idx, 'issue_date', val)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group" style={{ flex: 1 }}>
|
<FormField label="Datum splatnosti" style={{ flex: 1 }}>
|
||||||
<label className="admin-form-label">Datum splatnosti</label>
|
|
||||||
<AdminDatePicker
|
<AdminDatePicker
|
||||||
mode="date"
|
mode="date"
|
||||||
value={uploadMeta[idx]?.due_date || ''}
|
value={uploadMeta[idx]?.due_date || ''}
|
||||||
onChange={(val) => updateMeta(idx, 'due_date', val)}
|
onChange={(val) => updateMeta(idx, 'due_date', val)}
|
||||||
/>
|
/>
|
||||||
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<FormField label="Poznámka">
|
||||||
<div className="admin-form-group">
|
|
||||||
<label className="admin-form-label">Poznámka</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
value={uploadMeta[idx]?.notes || ''}
|
value={uploadMeta[idx]?.notes || ''}
|
||||||
onChange={(e) => updateMeta(idx, 'notes', e.target.value)}
|
onChange={(e) => updateMeta(idx, 'notes', e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@@ -801,8 +788,7 @@ export default function ReceivedInvoicesProps({ statsMonth, statsYear, uploadOpe
|
|||||||
</div>
|
</div>
|
||||||
<div className="admin-modal-body">
|
<div className="admin-modal-body">
|
||||||
<div className="admin-form">
|
<div className="admin-form">
|
||||||
<div className="admin-form-group">
|
<FormField label="Dodavatel" required>
|
||||||
<label className="admin-form-label required">Dodavatel</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
@@ -810,9 +796,8 @@ export default function ReceivedInvoicesProps({ statsMonth, statsYear, uploadOpe
|
|||||||
onChange={(e) => setEditInvoice(prev => ({ ...prev, supplier_name: e.target.value }))}
|
onChange={(e) => setEditInvoice(prev => ({ ...prev, supplier_name: e.target.value }))}
|
||||||
readOnly={ro}
|
readOnly={ro}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Č. faktury">
|
||||||
<label className="admin-form-label">Č. faktury</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
@@ -820,10 +805,9 @@ export default function ReceivedInvoicesProps({ statsMonth, statsYear, uploadOpe
|
|||||||
onChange={(e) => setEditInvoice(prev => ({ ...prev, invoice_number: e.target.value }))}
|
onChange={(e) => setEditInvoice(prev => ({ ...prev, invoice_number: e.target.value }))}
|
||||||
readOnly={ro}
|
readOnly={ro}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-row admin-form-row-3">
|
<div className="admin-form-row admin-form-row-3">
|
||||||
<div className="admin-form-group">
|
<FormField label="Částka" required>
|
||||||
<label className="admin-form-label required">Částka</label>
|
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
step="0.01"
|
step="0.01"
|
||||||
@@ -833,9 +817,8 @@ export default function ReceivedInvoicesProps({ statsMonth, statsYear, uploadOpe
|
|||||||
onChange={(e) => setEditInvoice(prev => ({ ...prev, amount: e.target.value }))}
|
onChange={(e) => setEditInvoice(prev => ({ ...prev, amount: e.target.value }))}
|
||||||
readOnly={ro}
|
readOnly={ro}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Měna">
|
||||||
<label className="admin-form-label">Měna</label>
|
|
||||||
<select
|
<select
|
||||||
className="admin-form-select"
|
className="admin-form-select"
|
||||||
value={editInvoice.currency}
|
value={editInvoice.currency}
|
||||||
@@ -844,9 +827,8 @@ export default function ReceivedInvoicesProps({ statsMonth, statsYear, uploadOpe
|
|||||||
>
|
>
|
||||||
{CURRENCY_OPTIONS.map(c => <option key={c} value={c}>{c}</option>)}
|
{CURRENCY_OPTIONS.map(c => <option key={c} value={c}>{c}</option>)}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="DPH %">
|
||||||
<label className="admin-form-label">DPH %</label>
|
|
||||||
<select
|
<select
|
||||||
className="admin-form-select"
|
className="admin-form-select"
|
||||||
value={editInvoice.vat_rate}
|
value={editInvoice.vat_rate}
|
||||||
@@ -855,7 +837,7 @@ export default function ReceivedInvoicesProps({ statsMonth, statsYear, uploadOpe
|
|||||||
>
|
>
|
||||||
{VAT_RATE_OPTIONS.map(r => <option key={r} value={String(r)}>{r}%</option>)}
|
{VAT_RATE_OPTIONS.map(r => <option key={r} value={String(r)}>{r}%</option>)}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
{editInvoice.amount && (
|
{editInvoice.amount && (
|
||||||
<div style={{ fontSize: '0.75rem', color: 'var(--text-tertiary)', marginBottom: '0.75rem' }}>
|
<div style={{ fontSize: '0.75rem', color: 'var(--text-tertiary)', marginBottom: '0.75rem' }}>
|
||||||
@@ -866,27 +848,24 @@ export default function ReceivedInvoicesProps({ statsMonth, statsYear, uploadOpe
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="admin-form-row">
|
<div className="admin-form-row">
|
||||||
<div className="admin-form-group">
|
<FormField label="Datum vystavení">
|
||||||
<label className="admin-form-label">Datum vystavení</label>
|
|
||||||
<AdminDatePicker
|
<AdminDatePicker
|
||||||
mode="date"
|
mode="date"
|
||||||
value={editInvoice.issue_date || ''}
|
value={editInvoice.issue_date || ''}
|
||||||
onChange={(val) => setEditInvoice(prev => ({ ...prev, issue_date: val }))}
|
onChange={(val) => setEditInvoice(prev => ({ ...prev, issue_date: val }))}
|
||||||
disabled={ro}
|
disabled={ro}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Datum splatnosti">
|
||||||
<label className="admin-form-label">Datum splatnosti</label>
|
|
||||||
<AdminDatePicker
|
<AdminDatePicker
|
||||||
mode="date"
|
mode="date"
|
||||||
value={editInvoice.due_date || ''}
|
value={editInvoice.due_date || ''}
|
||||||
onChange={(val) => setEditInvoice(prev => ({ ...prev, due_date: val }))}
|
onChange={(val) => setEditInvoice(prev => ({ ...prev, due_date: val }))}
|
||||||
disabled={ro}
|
disabled={ro}
|
||||||
/>
|
/>
|
||||||
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<FormField label="Stav">
|
||||||
<div className="admin-form-group">
|
|
||||||
<label className="admin-form-label">Stav</label>
|
|
||||||
<select
|
<select
|
||||||
className="admin-form-select"
|
className="admin-form-select"
|
||||||
value={editInvoice.status}
|
value={editInvoice.status}
|
||||||
@@ -896,9 +875,8 @@ export default function ReceivedInvoicesProps({ statsMonth, statsYear, uploadOpe
|
|||||||
<option value="unpaid">Neuhrazena</option>
|
<option value="unpaid">Neuhrazena</option>
|
||||||
<option value="paid">Uhrazena</option>
|
<option value="paid">Uhrazena</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Poznámka">
|
||||||
<label className="admin-form-label">Poznámka</label>
|
|
||||||
<textarea
|
<textarea
|
||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
rows={3}
|
rows={3}
|
||||||
@@ -906,7 +884,7 @@ export default function ReceivedInvoicesProps({ statsMonth, statsYear, uploadOpe
|
|||||||
onChange={(e) => setEditInvoice(prev => ({ ...prev, notes: e.target.value }))}
|
onChange={(e) => setEditInvoice(prev => ({ ...prev, notes: e.target.value }))}
|
||||||
readOnly={ro}
|
readOnly={ro}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="admin-modal-footer">
|
<div className="admin-modal-footer">
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { useAuth } from '../context/AuthContext'
|
|||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { motion, AnimatePresence } from 'framer-motion'
|
import { motion, AnimatePresence } from 'framer-motion'
|
||||||
import ConfirmModal from '../components/ConfirmModal'
|
import ConfirmModal from '../components/ConfirmModal'
|
||||||
|
import FormField from '../components/FormField'
|
||||||
import useModalLock from '../hooks/useModalLock'
|
import useModalLock from '../hooks/useModalLock'
|
||||||
|
|
||||||
import apiFetch from '../utils/api'
|
import apiFetch from '../utils/api'
|
||||||
@@ -496,8 +497,7 @@ export default function Settings() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="admin-form-group">
|
<FormField label="Zobrazovaný název">
|
||||||
<label className="admin-form-label">Zobrazovaný název</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={form.display_name}
|
value={form.display_name}
|
||||||
@@ -506,10 +506,9 @@ export default function Settings() {
|
|||||||
placeholder="např. Manažer"
|
placeholder="např. Manažer"
|
||||||
disabled={editingRole && isAdminRole(editingRole)}
|
disabled={editingRole && isAdminRole(editingRole)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
|
|
||||||
<div className="admin-form-group">
|
<FormField label="Systémový název (slug)">
|
||||||
<label className="admin-form-label">Systémový název (slug)</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={form.name}
|
value={form.name}
|
||||||
@@ -523,10 +522,9 @@ export default function Settings() {
|
|||||||
Pouze malá písmena, čísla a pomlčky. Nelze později změnit.
|
Pouze malá písmena, čísla a pomlčky. Nelze později změnit.
|
||||||
</small>
|
</small>
|
||||||
)}
|
)}
|
||||||
</div>
|
</FormField>
|
||||||
|
|
||||||
<div className="admin-form-group">
|
<FormField label="Popis">
|
||||||
<label className="admin-form-label">Popis</label>
|
|
||||||
<textarea
|
<textarea
|
||||||
value={form.description}
|
value={form.description}
|
||||||
onChange={(e) => setForm(prev => ({ ...prev, description: e.target.value }))}
|
onChange={(e) => setForm(prev => ({ ...prev, description: e.target.value }))}
|
||||||
@@ -535,7 +533,7 @@ export default function Settings() {
|
|||||||
placeholder="Volitelný popis role"
|
placeholder="Volitelný popis role"
|
||||||
disabled={editingRole && isAdminRole(editingRole)}
|
disabled={editingRole && isAdminRole(editingRole)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
|
|
||||||
<div className="admin-form-group">
|
<div className="admin-form-group">
|
||||||
<label className="admin-form-label" style={{ marginBottom: '0.75rem' }}>Oprávnění</label>
|
<label className="admin-form-label" style={{ marginBottom: '0.75rem' }}>Oprávnění</label>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { motion, AnimatePresence } from 'framer-motion'
|
|||||||
|
|
||||||
import AdminDatePicker from '../components/AdminDatePicker'
|
import AdminDatePicker from '../components/AdminDatePicker'
|
||||||
import ConfirmModal from '../components/ConfirmModal'
|
import ConfirmModal from '../components/ConfirmModal'
|
||||||
|
import FormField from '../components/FormField'
|
||||||
import useModalLock from '../hooks/useModalLock'
|
import useModalLock from '../hooks/useModalLock'
|
||||||
import Forbidden from '../components/Forbidden'
|
import Forbidden from '../components/Forbidden'
|
||||||
import { formatDate } from '../utils/attendanceHelpers'
|
import { formatDate } from '../utils/attendanceHelpers'
|
||||||
@@ -479,8 +480,7 @@ export default function Trips() {
|
|||||||
<div className="admin-modal-body">
|
<div className="admin-modal-body">
|
||||||
<div className="admin-form">
|
<div className="admin-form">
|
||||||
<div className="admin-form-row">
|
<div className="admin-form-row">
|
||||||
<div className={`admin-form-group${errors.vehicle_id ? ' has-error' : ''}`}>
|
<FormField label="Vozidlo" error={errors.vehicle_id} required>
|
||||||
<label className="admin-form-label required">Vozidlo</label>
|
|
||||||
<select
|
<select
|
||||||
value={form.vehicle_id}
|
value={form.vehicle_id}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
@@ -496,11 +496,9 @@ export default function Trips() {
|
|||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
{errors.vehicle_id && <span className="admin-form-error">{errors.vehicle_id}</span>}
|
</FormField>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={`admin-form-group${errors.trip_date ? ' has-error' : ''}`}>
|
<FormField label="Datum jízdy" error={errors.trip_date} required>
|
||||||
<label className="admin-form-label required">Datum jízdy</label>
|
|
||||||
<AdminDatePicker
|
<AdminDatePicker
|
||||||
mode="date"
|
mode="date"
|
||||||
value={form.trip_date}
|
value={form.trip_date}
|
||||||
@@ -509,13 +507,11 @@ export default function Trips() {
|
|||||||
setErrors(prev => ({ ...prev, trip_date: undefined }))
|
setErrors(prev => ({ ...prev, trip_date: undefined }))
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{errors.trip_date && <span className="admin-form-error">{errors.trip_date}</span>}
|
</FormField>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="admin-form-row admin-form-row-3">
|
<div className="admin-form-row admin-form-row-3">
|
||||||
<div className={`admin-form-group${errors.start_km ? ' has-error' : ''}`}>
|
<FormField label="Počáteční stav km" error={errors.start_km} required>
|
||||||
<label className="admin-form-label required">Počáteční stav km</label>
|
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
inputMode="numeric"
|
inputMode="numeric"
|
||||||
@@ -527,11 +523,9 @@ export default function Trips() {
|
|||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
min="0"
|
min="0"
|
||||||
/>
|
/>
|
||||||
{errors.start_km && <span className="admin-form-error">{errors.start_km}</span>}
|
</FormField>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={`admin-form-group${errors.end_km ? ' has-error' : ''}`}>
|
<FormField label="Konečný stav km" error={errors.end_km} required>
|
||||||
<label className="admin-form-label required">Konečný stav km</label>
|
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
inputMode="numeric"
|
inputMode="numeric"
|
||||||
@@ -543,11 +537,9 @@ export default function Trips() {
|
|||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
min="0"
|
min="0"
|
||||||
/>
|
/>
|
||||||
{errors.end_km && <span className="admin-form-error">{errors.end_km}</span>}
|
</FormField>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="admin-form-group">
|
<FormField label="Vzdálenost">
|
||||||
<label className="admin-form-label">Vzdálenost</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={`${formatKm(calculateDistance())} km`}
|
value={`${formatKm(calculateDistance())} km`}
|
||||||
@@ -555,12 +547,11 @@ export default function Trips() {
|
|||||||
readOnly
|
readOnly
|
||||||
disabled
|
disabled
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="admin-form-row">
|
<div className="admin-form-row">
|
||||||
<div className={`admin-form-group${errors.route_from ? ' has-error' : ''}`}>
|
<FormField label="Místo odjezdu" error={errors.route_from} required>
|
||||||
<label className="admin-form-label required">Místo odjezdu</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={form.route_from}
|
value={form.route_from}
|
||||||
@@ -571,11 +562,9 @@ export default function Trips() {
|
|||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
placeholder="Např. Praha"
|
placeholder="Např. Praha"
|
||||||
/>
|
/>
|
||||||
{errors.route_from && <span className="admin-form-error">{errors.route_from}</span>}
|
</FormField>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={`admin-form-group${errors.route_to ? ' has-error' : ''}`}>
|
<FormField label="Místo příjezdu" error={errors.route_to} required>
|
||||||
<label className="admin-form-label required">Místo příjezdu</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={form.route_to}
|
value={form.route_to}
|
||||||
@@ -586,12 +575,10 @@ export default function Trips() {
|
|||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
placeholder="Např. Brno"
|
placeholder="Např. Brno"
|
||||||
/>
|
/>
|
||||||
{errors.route_to && <span className="admin-form-error">{errors.route_to}</span>}
|
</FormField>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="admin-form-group">
|
<FormField label="Typ jízdy">
|
||||||
<label className="admin-form-label">Typ jízdy</label>
|
|
||||||
<select
|
<select
|
||||||
value={form.is_business}
|
value={form.is_business}
|
||||||
onChange={(e) => setForm({ ...form, is_business: parseInt(e.target.value) })}
|
onChange={(e) => setForm({ ...form, is_business: parseInt(e.target.value) })}
|
||||||
@@ -600,10 +587,9 @@ export default function Trips() {
|
|||||||
<option value={1}>Služební</option>
|
<option value={1}>Služební</option>
|
||||||
<option value={0}>Soukromá</option>
|
<option value={0}>Soukromá</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</FormField>
|
||||||
|
|
||||||
<div className="admin-form-group">
|
<FormField label="Poznámky">
|
||||||
<label className="admin-form-label">Poznámky</label>
|
|
||||||
<textarea
|
<textarea
|
||||||
value={form.notes}
|
value={form.notes}
|
||||||
onChange={(e) => setForm({ ...form, notes: e.target.value })}
|
onChange={(e) => setForm({ ...form, notes: e.target.value })}
|
||||||
@@ -611,7 +597,7 @@ export default function Trips() {
|
|||||||
rows={2}
|
rows={2}
|
||||||
placeholder="Volitelné poznámky..."
|
placeholder="Volitelné poznámky..."
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { motion, AnimatePresence } from 'framer-motion'
|
|||||||
import ConfirmModal from '../components/ConfirmModal'
|
import ConfirmModal from '../components/ConfirmModal'
|
||||||
|
|
||||||
import AdminDatePicker from '../components/AdminDatePicker'
|
import AdminDatePicker from '../components/AdminDatePicker'
|
||||||
|
import FormField from '../components/FormField'
|
||||||
import useModalLock from '../hooks/useModalLock'
|
import useModalLock from '../hooks/useModalLock'
|
||||||
import { formatDate } from '../utils/attendanceHelpers'
|
import { formatDate } from '../utils/attendanceHelpers'
|
||||||
import { formatKm } from '../utils/formatters'
|
import { formatKm } from '../utils/formatters'
|
||||||
@@ -302,24 +303,21 @@ export default function TripsAdmin() {
|
|||||||
>
|
>
|
||||||
<div className="admin-card-body">
|
<div className="admin-card-body">
|
||||||
<div className="admin-form-row admin-form-row-4">
|
<div className="admin-form-row admin-form-row-4">
|
||||||
<div className="admin-form-group" style={{ marginBottom: 0 }}>
|
<FormField label="Od" style={{ marginBottom: 0 }}>
|
||||||
<label className="admin-form-label">Od</label>
|
|
||||||
<AdminDatePicker
|
<AdminDatePicker
|
||||||
mode="date"
|
mode="date"
|
||||||
value={dateFrom}
|
value={dateFrom}
|
||||||
onChange={(val) => setDateFrom(val)}
|
onChange={(val) => setDateFrom(val)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group" style={{ marginBottom: 0 }}>
|
<FormField label="Do" style={{ marginBottom: 0 }}>
|
||||||
<label className="admin-form-label">Do</label>
|
|
||||||
<AdminDatePicker
|
<AdminDatePicker
|
||||||
mode="date"
|
mode="date"
|
||||||
value={dateTo}
|
value={dateTo}
|
||||||
onChange={(val) => setDateTo(val)}
|
onChange={(val) => setDateTo(val)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group" style={{ marginBottom: 0 }}>
|
<FormField label="Vozidlo" style={{ marginBottom: 0 }}>
|
||||||
<label className="admin-form-label">Vozidlo</label>
|
|
||||||
<select
|
<select
|
||||||
value={filterVehicleId}
|
value={filterVehicleId}
|
||||||
onChange={(e) => setFilterVehicleId(e.target.value)}
|
onChange={(e) => setFilterVehicleId(e.target.value)}
|
||||||
@@ -332,9 +330,8 @@ export default function TripsAdmin() {
|
|||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group" style={{ marginBottom: 0 }}>
|
<FormField label="Řidič" style={{ marginBottom: 0 }}>
|
||||||
<label className="admin-form-label">Řidič</label>
|
|
||||||
<select
|
<select
|
||||||
value={filterUserId}
|
value={filterUserId}
|
||||||
onChange={(e) => setFilterUserId(e.target.value)}
|
onChange={(e) => setFilterUserId(e.target.value)}
|
||||||
@@ -347,7 +344,7 @@ export default function TripsAdmin() {
|
|||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
@@ -527,8 +524,7 @@ export default function TripsAdmin() {
|
|||||||
<div className="admin-modal-body">
|
<div className="admin-modal-body">
|
||||||
<div className="admin-form">
|
<div className="admin-form">
|
||||||
<div className="admin-form-row">
|
<div className="admin-form-row">
|
||||||
<div className="admin-form-group">
|
<FormField label="Vozidlo">
|
||||||
<label className="admin-form-label">Vozidlo</label>
|
|
||||||
<select
|
<select
|
||||||
value={editForm.vehicle_id}
|
value={editForm.vehicle_id}
|
||||||
onChange={(e) => setEditForm({ ...editForm, vehicle_id: e.target.value })}
|
onChange={(e) => setEditForm({ ...editForm, vehicle_id: e.target.value })}
|
||||||
@@ -540,21 +536,19 @@ export default function TripsAdmin() {
|
|||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</FormField>
|
||||||
|
|
||||||
<div className="admin-form-group">
|
<FormField label="Datum jízdy">
|
||||||
<label className="admin-form-label">Datum jízdy</label>
|
|
||||||
<AdminDatePicker
|
<AdminDatePicker
|
||||||
mode="date"
|
mode="date"
|
||||||
value={editForm.trip_date}
|
value={editForm.trip_date}
|
||||||
onChange={(val) => setEditForm({ ...editForm, trip_date: val })}
|
onChange={(val) => setEditForm({ ...editForm, trip_date: val })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="admin-form-row">
|
<div className="admin-form-row">
|
||||||
<div className="admin-form-group">
|
<FormField label="Počáteční stav km">
|
||||||
<label className="admin-form-label">Počáteční stav km</label>
|
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
inputMode="numeric"
|
inputMode="numeric"
|
||||||
@@ -563,10 +557,9 @@ export default function TripsAdmin() {
|
|||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
min="0"
|
min="0"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
|
|
||||||
<div className="admin-form-group">
|
<FormField label="Konečný stav km">
|
||||||
<label className="admin-form-label">Konečný stav km</label>
|
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
inputMode="numeric"
|
inputMode="numeric"
|
||||||
@@ -575,10 +568,9 @@ export default function TripsAdmin() {
|
|||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
min="0"
|
min="0"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
|
|
||||||
<div className="admin-form-group">
|
<FormField label="Vzdálenost">
|
||||||
<label className="admin-form-label">Vzdálenost</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={`${formatKm(calculateDistance())} km`}
|
value={`${formatKm(calculateDistance())} km`}
|
||||||
@@ -586,33 +578,30 @@ export default function TripsAdmin() {
|
|||||||
readOnly
|
readOnly
|
||||||
disabled
|
disabled
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="admin-form-row">
|
<div className="admin-form-row">
|
||||||
<div className="admin-form-group">
|
<FormField label="Místo odjezdu">
|
||||||
<label className="admin-form-label">Místo odjezdu</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={editForm.route_from}
|
value={editForm.route_from}
|
||||||
onChange={(e) => setEditForm({ ...editForm, route_from: e.target.value })}
|
onChange={(e) => setEditForm({ ...editForm, route_from: e.target.value })}
|
||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
|
|
||||||
<div className="admin-form-group">
|
<FormField label="Místo příjezdu">
|
||||||
<label className="admin-form-label">Místo příjezdu</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={editForm.route_to}
|
value={editForm.route_to}
|
||||||
onChange={(e) => setEditForm({ ...editForm, route_to: e.target.value })}
|
onChange={(e) => setEditForm({ ...editForm, route_to: e.target.value })}
|
||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="admin-form-group">
|
<FormField label="Typ jízdy">
|
||||||
<label className="admin-form-label">Typ jízdy</label>
|
|
||||||
<select
|
<select
|
||||||
value={editForm.is_business}
|
value={editForm.is_business}
|
||||||
onChange={(e) => setEditForm({ ...editForm, is_business: parseInt(e.target.value) })}
|
onChange={(e) => setEditForm({ ...editForm, is_business: parseInt(e.target.value) })}
|
||||||
@@ -621,17 +610,16 @@ export default function TripsAdmin() {
|
|||||||
<option value={1}>Služební</option>
|
<option value={1}>Služební</option>
|
||||||
<option value={0}>Soukromá</option>
|
<option value={0}>Soukromá</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</FormField>
|
||||||
|
|
||||||
<div className="admin-form-group">
|
<FormField label="Poznámky">
|
||||||
<label className="admin-form-label">Poznámky</label>
|
|
||||||
<textarea
|
<textarea
|
||||||
value={editForm.notes}
|
value={editForm.notes}
|
||||||
onChange={(e) => setEditForm({ ...editForm, notes: e.target.value })}
|
onChange={(e) => setEditForm({ ...editForm, notes: e.target.value })}
|
||||||
className="admin-form-textarea"
|
className="admin-form-textarea"
|
||||||
rows={2}
|
rows={2}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import AdminDatePicker from '../components/AdminDatePicker'
|
|||||||
import Forbidden from '../components/Forbidden'
|
import Forbidden from '../components/Forbidden'
|
||||||
import { formatDate } from '../utils/attendanceHelpers'
|
import { formatDate } from '../utils/attendanceHelpers'
|
||||||
import { formatKm } from '../utils/formatters'
|
import { formatKm } from '../utils/formatters'
|
||||||
|
import FormField from '../components/FormField'
|
||||||
import apiFetch from '../utils/api'
|
import apiFetch from '../utils/api'
|
||||||
|
|
||||||
const API_BASE = '/api/admin'
|
const API_BASE = '/api/admin'
|
||||||
@@ -82,16 +83,14 @@ export default function TripsHistory() {
|
|||||||
>
|
>
|
||||||
<div className="admin-card-body">
|
<div className="admin-card-body">
|
||||||
<div className="admin-form-row">
|
<div className="admin-form-row">
|
||||||
<div className="admin-form-group" style={{ marginBottom: 0 }}>
|
<FormField label="Měsíc">
|
||||||
<label className="admin-form-label">Měsíc</label>
|
|
||||||
<AdminDatePicker
|
<AdminDatePicker
|
||||||
mode="month"
|
mode="month"
|
||||||
value={month}
|
value={month}
|
||||||
onChange={(val) => setMonth(val)}
|
onChange={(val) => setMonth(val)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group" style={{ marginBottom: 0 }}>
|
<FormField label="Vozidlo">
|
||||||
<label className="admin-form-label">Vozidlo</label>
|
|
||||||
<select
|
<select
|
||||||
value={vehicleId}
|
value={vehicleId}
|
||||||
onChange={(e) => setVehicleId(e.target.value)}
|
onChange={(e) => setVehicleId(e.target.value)}
|
||||||
@@ -104,7 +103,7 @@ export default function TripsHistory() {
|
|||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { motion, AnimatePresence } from 'framer-motion'
|
|||||||
import { useAuth } from '../context/AuthContext'
|
import { useAuth } from '../context/AuthContext'
|
||||||
import { useAlert } from '../context/AlertContext'
|
import { useAlert } from '../context/AlertContext'
|
||||||
import ConfirmModal from '../components/ConfirmModal'
|
import ConfirmModal from '../components/ConfirmModal'
|
||||||
|
import FormField from '../components/FormField'
|
||||||
import Forbidden from '../components/Forbidden'
|
import Forbidden from '../components/Forbidden'
|
||||||
import useModalLock from '../hooks/useModalLock'
|
import useModalLock from '../hooks/useModalLock'
|
||||||
|
|
||||||
@@ -357,8 +358,7 @@ export default function Users() {
|
|||||||
<div className="admin-modal-body">
|
<div className="admin-modal-body">
|
||||||
<div className="admin-form">
|
<div className="admin-form">
|
||||||
<div className="admin-form-row">
|
<div className="admin-form-row">
|
||||||
<div className="admin-form-group">
|
<FormField label="Jméno">
|
||||||
<label className="admin-form-label">Jméno</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={formData.first_name}
|
value={formData.first_name}
|
||||||
@@ -366,9 +366,8 @@ export default function Users() {
|
|||||||
required
|
required
|
||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
<div className="admin-form-group">
|
<FormField label="Příjmení">
|
||||||
<label className="admin-form-label">Příjmení</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={formData.last_name}
|
value={formData.last_name}
|
||||||
@@ -376,11 +375,10 @@ export default function Users() {
|
|||||||
required
|
required
|
||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="admin-form-group">
|
<FormField label="Uživatelské jméno">
|
||||||
<label className="admin-form-label">Uživatelské jméno</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={formData.username}
|
value={formData.username}
|
||||||
@@ -388,10 +386,9 @@ export default function Users() {
|
|||||||
required
|
required
|
||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
|
|
||||||
<div className="admin-form-group">
|
<FormField label="E-mail">
|
||||||
<label className="admin-form-label">E-mail</label>
|
|
||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
value={formData.email}
|
value={formData.email}
|
||||||
@@ -399,12 +396,9 @@ export default function Users() {
|
|||||||
required
|
required
|
||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
|
|
||||||
<div className="admin-form-group">
|
<FormField label={`Heslo ${editingUser ? '(ponechte prázdné pro zachování stávajícího)' : ''}`}>
|
||||||
<label className="admin-form-label">
|
|
||||||
Heslo {editingUser && '(ponechte prázdné pro zachování stávajícího)'}
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
value={formData.password}
|
value={formData.password}
|
||||||
@@ -412,10 +406,9 @@ export default function Users() {
|
|||||||
required={!editingUser}
|
required={!editingUser}
|
||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormField>
|
||||||
|
|
||||||
<div className="admin-form-group">
|
<FormField label="Role">
|
||||||
<label className="admin-form-label">Role</label>
|
|
||||||
<select
|
<select
|
||||||
value={formData.role_id}
|
value={formData.role_id}
|
||||||
onChange={(e) => setFormData({ ...formData, role_id: e.target.value })}
|
onChange={(e) => setFormData({ ...formData, role_id: e.target.value })}
|
||||||
@@ -428,7 +421,7 @@ export default function Users() {
|
|||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</FormField>
|
||||||
|
|
||||||
<label className="admin-form-checkbox">
|
<label className="admin-form-checkbox">
|
||||||
<input
|
<input
|
||||||
|
|||||||
Reference in New Issue
Block a user