fix: update custom fields in DocumentGenerator and DOCX_TEMPLATES_README for better usability

This commit is contained in:
2025-12-16 11:23:50 +01:00
parent 1fb435eb87
commit 75b8bfd84f
2 changed files with 97 additions and 25 deletions

View File

@@ -49,11 +49,16 @@ This system allows you to generate DOCX documents by filling templates with proj
#### Financial #### Financial
- `{wartosc_zlecenia}`, `{wartosc_zlecenia_1}`, `{wartosc_zlecenia_2}` - Contract value - `{wartosc_zlecenia}`, `{wartosc_zlecenia_1}`, `{wartosc_zlecenia_2}` - Contract value
#### Contacts #### Standard Custom Fields (Pre-filled but Editable)
- `{contacts}` - Array of contacts (use loops in advanced templates) - `{zk}` - ZK field
- `{primary_contact}` - Primary contact name - `{nr_zk}` - ZK number
- `{primary_contact_phone}` - Primary contact phone - `{kabel}` - Cable information
- `{primary_contact_email}` - Primary contact email - `{dlugosc}` - Length
- `{data_wykonania}` - Execution date
- `{st_nr}` - Station number
- `{obw}` - Circuit
- `{wp_short}` - Short WP reference
- `{plomba}` - Seal/plomb information
## Example Template Content ## Example Template Content
@@ -78,6 +83,17 @@ Meeting Notes: {meeting_notes}
Special Instructions: {special_instructions} Special Instructions: {special_instructions}
Additional Comments: {additional_comments} Additional Comments: {additional_comments}
Technical Details:
ZK: {zk}
ZK Number: {nr_zk}
Cable: {kabel}
Length: {dlugosc}
Execution Date: {data_wykonania}
Station Number: {st_nr}
Circuit: {obw}
WP Short: {wp_short}
Seal: {plomba}
Primary Contact: Primary Contact:
Name: {primary_contact} Name: {primary_contact}
Phone: {primary_contact_phone} Phone: {primary_contact_phone}
@@ -100,7 +116,7 @@ Generated on: {today_date}
2. In the sidebar, find the "Generate Document" section 2. In the sidebar, find the "Generate Document" section
3. Select a template from the dropdown 3. Select a template from the dropdown
4. **Optional**: Click "Pokaż dodatkowe pola" to add custom data 4. **Optional**: Click "Pokaż dodatkowe pola" to add custom data
5. Add any custom fields you need (e.g., `custom_note`, `additional_info`) 5. Fill in the standard fields (zk, nr_zk, kabel, etc.) and any additional custom fields
6. Click "Generate Document" 6. Click "Generate Document"
7. The filled document will be downloaded automatically with filename: `{template_name}_{project_name}_{timestamp}.docx` 7. The filled document will be downloaded automatically with filename: `{template_name}_{project_name}_{timestamp}.docx`
@@ -108,10 +124,16 @@ Generated on: {today_date}
During document generation, you can add custom data that will be merged with the project data: During document generation, you can add custom data that will be merged with the project data:
### Standard Fields (Pre-filled but Fully Editable)
These fields are pre-filled with common names but can be modified or removed:
- `zk`, `nr_zk`, `kabel`, `dlugosc`, `data_wykonania`, `st_nr`, `obw`, `wp_short`, `plomba`
### Additional Custom Fields
- **Custom fields** override project data if they have the same name - **Custom fields** override project data if they have the same name
- Use descriptive names like `meeting_notes`, `special_instructions`, `custom_date` - Use descriptive names like `meeting_notes`, `special_instructions`, `custom_date`
- Custom fields are available in templates as `{custom_field_name}` - Custom fields are available in templates as `{custom_field_name}`
- Empty custom fields are ignored - Empty custom fields are ignored
- All fields can be removed if not needed
### Example Custom Fields: ### Example Custom Fields:
- `meeting_notes`: "Please bring project documentation" - `meeting_notes`: "Please bring project documentation"

View File

@@ -9,7 +9,17 @@ export default function DocumentGenerator({ projectId }) {
const [selectedTemplate, setSelectedTemplate] = useState(""); const [selectedTemplate, setSelectedTemplate] = useState("");
const [generating, setGenerating] = useState(false); const [generating, setGenerating] = useState(false);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [customFields, setCustomFields] = useState([{ key: "", value: "" }]); const [customFields, setCustomFields] = useState([
{ key: "zk", value: "" },
{ key: "nr_zk", value: "" },
{ key: "kabel", value: "" },
{ key: "dlugosc", value: "" },
{ key: "data_wykonania", value: "" },
{ key: "st_nr", value: "" },
{ key: "obw", value: "" },
{ key: "wp_short", value: "" },
{ key: "plomba", value: "" }
]);
const [showCustomFields, setShowCustomFields] = useState(false); const [showCustomFields, setShowCustomFields] = useState(false);
useEffect(() => { useEffect(() => {
@@ -41,6 +51,7 @@ export default function DocumentGenerator({ projectId }) {
}; };
const removeCustomField = (index) => { const removeCustomField = (index) => {
// Allow removing any field, but keep at least one empty field
if (customFields.length > 1) { if (customFields.length > 1) {
setCustomFields(customFields.filter((_, i) => i !== index)); setCustomFields(customFields.filter((_, i) => i !== index));
} }
@@ -183,11 +194,14 @@ export default function DocumentGenerator({ projectId }) {
{showCustomFields && ( {showCustomFields && (
<div className="space-y-2 border border-gray-200 rounded-md p-3 bg-gray-50"> <div className="space-y-2 border border-gray-200 rounded-md p-3 bg-gray-50">
{customFields.map((field, index) => ( <div className="text-xs text-gray-600 font-medium mb-2">
Standardowe pola (można edytować i usuwać):
</div>
{customFields.slice(0, 9).map((field, index) => (
<div key={index} className="flex gap-2 items-center"> <div key={index} className="flex gap-2 items-center">
<input <input
type="text" type="text"
placeholder="Nazwa pola (np. custom_note)" placeholder="Nazwa pola"
value={field.key} value={field.key}
onChange={(e) => handleCustomFieldChange(index, 'key', e.target.value)} onChange={(e) => handleCustomFieldChange(index, 'key', e.target.value)}
className="flex-1 px-2 py-1 text-sm border border-gray-300 rounded focus:ring-1 focus:ring-blue-500 focus:border-transparent" className="flex-1 px-2 py-1 text-sm border border-gray-300 rounded focus:ring-1 focus:ring-blue-500 focus:border-transparent"
@@ -199,32 +213,68 @@ export default function DocumentGenerator({ projectId }) {
onChange={(e) => handleCustomFieldChange(index, 'value', e.target.value)} onChange={(e) => handleCustomFieldChange(index, 'value', e.target.value)}
className="flex-1 px-2 py-1 text-sm border border-gray-300 rounded focus:ring-1 focus:ring-blue-500 focus:border-transparent" className="flex-1 px-2 py-1 text-sm border border-gray-300 rounded focus:ring-1 focus:ring-blue-500 focus:border-transparent"
/> />
{customFields.length > 1 && ( <Button
<Button type="button"
type="button" variant="ghost"
variant="ghost" size="sm"
size="sm" onClick={() => removeCustomField(index)}
onClick={() => removeCustomField(index)} className="text-red-600 hover:text-red-800 p-1"
className="text-red-600 hover:text-red-800 p-1" >
> <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" /> </svg>
</svg> </Button>
</Button>
)}
</div> </div>
))} ))}
{customFields.length > 9 && (
<>
<div className="text-xs text-gray-600 font-medium mt-4 mb-2">
Dodatkowe pola:
</div>
{customFields.slice(9).map((field, index) => (
<div key={index + 9} className="flex gap-2 items-center">
<input
type="text"
placeholder="Nazwa pola"
value={field.key}
onChange={(e) => handleCustomFieldChange(index + 9, 'key', e.target.value)}
className="flex-1 px-2 py-1 text-sm border border-gray-300 rounded focus:ring-1 focus:ring-blue-500 focus:border-transparent"
/>
<input
type="text"
placeholder="Wartość"
value={field.value}
onChange={(e) => handleCustomFieldChange(index + 9, 'value', e.target.value)}
className="flex-1 px-2 py-1 text-sm border border-gray-300 rounded focus:ring-1 focus:ring-blue-500 focus:border-transparent"
/>
<Button
type="button"
variant="ghost"
size="sm"
onClick={() => removeCustomField(index + 9)}
className="text-red-600 hover:text-red-800 p-1"
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</Button>
</div>
))}
</>
)}
<Button <Button
type="button" type="button"
variant="outline" variant="outline"
size="sm" size="sm"
onClick={addCustomField} onClick={addCustomField}
className="w-full text-xs" className="w-full text-xs mt-2"
> >
+ Dodaj pole + Dodaj dodatkowe pole
</Button> </Button>
<div className="text-xs text-gray-500 mt-2"> <div className="text-xs text-gray-500 mt-2">
Wprowadź dodatkowe dane, które będą dostępne w szablonie jako {"{custom_note}"}, {"{additional_info}"} itp. Standardowe pola wstępnie wypełnione, ale można je edytować lub usuwać. Wszystkie pola będą dostępne w szablonie jako {"{nazwa_pola}"}.
</div> </div>
</div> </div>
)} )}