fix: improve ui; clean unused component

This commit is contained in:
Aleix Soler 2025-08-06 18:44:21 +02:00
parent ddcb49ab84
commit d47c76c0ca
10 changed files with 75 additions and 377 deletions

View file

@ -74,8 +74,6 @@ export const TemplateEditor: React.FC<TemplateEditorProps> = ({
id: template?.id || "", // Will be generated in integration
name: templateName.trim(),
description: templateDescription.trim(),
isPredefined: template?.isPredefined || false,
isEnabled: template?.isEnabled ?? true,
statuses: statuses,
};

View file

@ -52,7 +52,6 @@ export const TemplateEditorModal: React.FC<TemplateEditorModalProps> = ({
name: name.trim(),
description: description.trim(),
statuses: statusesWithTemplateId,
isCustom: true,
};
onSave(savedTemplate);

View file

@ -78,15 +78,18 @@ export const TemplateItem: React.FC<TemplateItemProps> = ({
title="Edit template"
>
<svg
width="16"
height="16"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" />
<path d="m18.5 2.5 4 4L13 16l-4-4L18.5 2.5z" />
<path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z" />
<path d="m15 5 4 4" />
</svg>
</button>
)}
@ -100,18 +103,21 @@ export const TemplateItem: React.FC<TemplateItemProps> = ({
title="Delete template"
>
<svg
width="16"
height="16"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="3,6 5,6 21,6" />
<path d="M19,6l-2,14a2,2,0,0,1-2,2H9a2,2,0,0,1-2-2L5,6" />
<path d="M10,11V17" />
<path d="M14,11V17" />
<path d="M9,6V4a2,2,0,0,1,2-2h2a2,2,0,0,1,2,2V6" />
<path d="M10 11v6" />
<path d="M14 11v6" />
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" />
<path d="M3 6h18" />
<path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
</svg>
</button>
)}

View file

@ -6,11 +6,14 @@ import {
generateTemplateId,
isTemplateNameUnique,
} from "@/utils/templateUtils";
import {
DEFAULT_ENABLED_TEMPLATES,
PREDEFINED_TEMPLATES,
} from "@/constants/predefinedTemplates";
interface TemplateSettingsProps {
settings: PluginSettings;
onChange: (key: keyof PluginSettings, value: unknown) => void;
templates: StatusTemplate[];
}
export const TemplateSettings: React.FC<TemplateSettingsProps> = ({
@ -124,8 +127,8 @@ export const TemplateSettings: React.FC<TemplateSettingsProps> = ({
);
if (!confirmed) return;
onChange("templates", []);
onChange("enabledTemplates", ["colorful"]);
onChange("templates", [...PREDEFINED_TEMPLATES]);
onChange("enabledTemplates", [...DEFAULT_ENABLED_TEMPLATES]);
}, [onChange]);
const handleCancelEditor = useCallback(() => {

View file

@ -1,139 +0,0 @@
import React from "react";
import { StatusTemplate } from "../../types/pluginSettings";
import { StatusDisplay } from "../atoms/StatusDisplay";
interface UnifiedTemplateItemProps {
template: StatusTemplate;
onToggle: (templateId: string, enabled: boolean) => void;
onEdit: (template: StatusTemplate) => void;
onDelete: (templateId: string) => void;
onReset: (templateId: string) => void;
onDuplicate: (templateId: string) => void;
}
export const UnifiedTemplateItem: React.FC<UnifiedTemplateItemProps> = ({
template,
onToggle,
onEdit,
onDelete,
onReset,
onDuplicate,
}) => {
const handleToggle = () => {
onToggle(template.id, !template.isEnabled);
};
const handleEdit = () => {
onEdit(template);
};
const handleDelete = () => {
if (
confirm(
`Are you sure you want to delete the template "${template.name}"?`,
)
) {
onDelete(template.id);
}
};
const handleReset = () => {
if (
confirm(
`Reset "${template.name}" to its original state? This will lose any customizations.`,
)
) {
onReset(template.id);
}
};
const handleDuplicate = () => {
onDuplicate(template.id);
};
return (
<div
className={`unified-template-item ${template.isEnabled ? "unified-template-item--enabled" : ""}`}
>
<div className="unified-template-item__main" onClick={handleToggle}>
<div className="unified-template-item__checkbox-container">
<input
type="checkbox"
className="unified-template-item__checkbox"
checked={template.isEnabled || false}
readOnly
/>
</div>
<div className="unified-template-item__content">
<div className="unified-template-item__header">
<span className="unified-template-item__name">
{template.name}
</span>
{template.isPredefined && (
<span className="unified-template-item__badge">
Predefined
</span>
)}
</div>
<div className="unified-template-item__description">
{template.description}
</div>
<div className="unified-template-item__statuses">
{template.statuses.map((status, index) => (
<StatusDisplay
key={index}
status={status}
variant="template"
/>
))}
</div>
</div>
</div>
<div className="unified-template-item__actions">
<button
className="unified-template-item__action-btn unified-template-item__action-btn--edit"
onClick={handleEdit}
aria-label="Edit template"
title="Edit template"
>
</button>
<button
className="unified-template-item__action-btn unified-template-item__action-btn--duplicate"
onClick={handleDuplicate}
aria-label="Duplicate template"
title="Duplicate template"
>
📋
</button>
{template.isPredefined && (
<button
className="unified-template-item__action-btn unified-template-item__action-btn--reset"
onClick={handleReset}
aria-label="Reset to default"
title="Reset to default"
>
🔄
</button>
)}
{!template.isPredefined && (
<button
className="unified-template-item__action-btn unified-template-item__action-btn--delete"
onClick={handleDelete}
aria-label="Delete template"
title="Delete template"
>
🗑
</button>
)}
</div>
</div>
);
};

View file

@ -1,202 +0,0 @@
import React, { useState, useCallback } from "react";
import { PluginSettings, StatusTemplate } from "../../types/pluginSettings";
import { TemplateIntegration } from "../../integrations/templates/templateIntegration";
import { UnifiedTemplateItem } from "./UnifiedTemplateItem";
import { TemplateEditor } from "./TemplateEditor";
interface UnifiedTemplateSettingsProps {
settings: PluginSettings;
onChange: (key: keyof PluginSettings, value: unknown) => void;
}
export const UnifiedTemplateSettings: React.FC<
UnifiedTemplateSettingsProps
> = ({ settings, onChange }) => {
const [isCreating, setIsCreating] = useState(false);
const [editingTemplate, setEditingTemplate] =
useState<StatusTemplate | null>(null);
const handleCreateTemplate = useCallback(() => {
setIsCreating(true);
}, []);
const handleEditTemplate = useCallback((template: StatusTemplate) => {
setEditingTemplate(template);
}, []);
const handleDeleteTemplate = useCallback(
(templateId: string) => {
const success = TemplateIntegration.deleteTemplate(templateId);
if (success) {
// Trigger settings refresh
onChange("templates", TemplateIntegration.getTemplates());
}
},
[onChange],
);
const handleResetTemplate = useCallback(
(templateId: string) => {
const success =
TemplateIntegration.resetPredefinedTemplate(templateId);
if (success) {
// Trigger settings refresh
onChange("templates", TemplateIntegration.getTemplates());
}
},
[onChange],
);
const handleDuplicateTemplate = useCallback(
(templateId: string) => {
const newTemplate =
TemplateIntegration.duplicateTemplate(templateId);
if (newTemplate) {
// Trigger settings refresh
onChange("templates", TemplateIntegration.getTemplates());
}
},
[onChange],
);
const handleToggleTemplate = useCallback(
(templateId: string, isEnabled: boolean) => {
TemplateIntegration.toggleTemplate(templateId, isEnabled);
// Trigger settings refresh
onChange("templates", TemplateIntegration.getTemplates());
},
[onChange],
);
const handleSaveTemplate = useCallback(
(template: StatusTemplate) => {
if (editingTemplate) {
// Update existing template
const success = TemplateIntegration.updateTemplate(
editingTemplate.id,
template,
);
if (success) {
onChange("templates", TemplateIntegration.getTemplates());
}
} else {
// Create new template
TemplateIntegration.createTemplate(
template.name,
template.description,
template.statuses,
);
onChange("templates", TemplateIntegration.getTemplates());
}
setIsCreating(false);
setEditingTemplate(null);
},
[editingTemplate, onChange],
);
const handleCancel = useCallback(() => {
setIsCreating(false);
setEditingTemplate(null);
}, []);
const predefinedTemplates = TemplateIntegration.getPredefinedTemplates();
const customTemplates = TemplateIntegration.getCustomTemplates();
return (
<div className="unified-template-settings">
<div className="setting-item-info">
<div className="setting-item-name">Template Management</div>
<div className="setting-item-description">
Manage predefined and custom status templates. You can edit,
enable/disable, and reset predefined templates.
</div>
</div>
{(isCreating || editingTemplate) && (
<TemplateEditor
template={editingTemplate}
onSave={handleSaveTemplate}
onCancel={handleCancel}
existingTemplateNames={(settings.templates || [])
.filter((t) => t.id !== editingTemplate?.id)
.map((t) => t.name.toLowerCase())}
settings={settings}
/>
)}
{!isCreating && !editingTemplate && (
<>
<div className="template-actions-bar">
<button
className="mod-cta"
onClick={handleCreateTemplate}
>
Create New Template
</button>
</div>
{predefinedTemplates.length > 0 && (
<div className="template-section">
<h4 className="template-section__title">
Predefined Templates
</h4>
<p className="template-section__description">
Built-in templates that can be customized,
enabled/disabled, or reset to defaults.
</p>
<div className="template-list">
{predefinedTemplates.map((template) => (
<UnifiedTemplateItem
key={template.id}
template={template}
onToggle={handleToggleTemplate}
onEdit={handleEditTemplate}
onDelete={handleDeleteTemplate}
onReset={handleResetTemplate}
onDuplicate={handleDuplicateTemplate}
/>
))}
</div>
</div>
)}
{customTemplates.length > 0 && (
<div className="template-section">
<h4 className="template-section__title">
Custom Templates
</h4>
<p className="template-section__description">
Your custom templates that you've created.
</p>
<div className="template-list">
{customTemplates.map((template) => (
<UnifiedTemplateItem
key={template.id}
template={template}
onToggle={handleToggleTemplate}
onEdit={handleEditTemplate}
onDelete={handleDeleteTemplate}
onReset={handleResetTemplate}
onDuplicate={handleDuplicateTemplate}
/>
))}
</div>
</div>
)}
{customTemplates.length === 0 && (
<div className="template-section">
<div className="template-list--empty">
<p>No custom templates created yet.</p>
<p>
Click "Create New Template" to get started.
</p>
</div>
</div>
)}
</>
)}
</div>
);
};

View file

@ -31,7 +31,8 @@ export class CommandsIntegration {
({ key }) => {
if (
key === "quickStatusCommands" ||
key === "useMultipleStatuses"
key === "useMultipleStatuses" ||
key === "templates"
) {
this.commandsService.destroy();
/// BUG: if removed a command will persist because is not removed, you need the oldStates to send it to be disabled // const oldValue = this.settings[key]; // TODO: Send the old value

View file

@ -57,7 +57,8 @@ export class FileExplorerIntegration implements IElementProcessor {
key === "strictStatuses" ||
key === "fileExplorerIconPosition" ||
key === "unknownStatusIcon" ||
key === "unknownStatusColor"
key === "unknownStatusColor" ||
key === "templates"
) {
this.destroy();
this.integrate().catch((r) => console.error(r));

View file

@ -43,7 +43,7 @@ export class StatusBarIntegration {
if (key === "showStatusBar") {
this.render(); // INFO: Force a render to set disabled or enabled
}
if (key === "enabledTemplates") {
if (key === "enabledTemplates" || key === "templates") {
this.handleActiveFileChange().catch(console.error); // INFO: Force a re-read of the statuses and render
}
if (key === "autoHideStatusBar") {

View file

@ -27,8 +27,8 @@
align-items: center;
gap: var(--size-2-2);
padding: var(--size-2-3) var(--size-4-3);
background: var(--background-modifier-border);
border: 1px solid var(--background-modifier-border-hover);
background: var(--interactive-normal);
border: 1px solid var(--background-modifier-border);
border-radius: var(--radius-s);
color: var(--text-normal);
cursor: pointer;
@ -37,8 +37,8 @@
}
.template-reset-btn:hover {
background: var(--background-modifier-border-hover);
color: var(--text-accent);
background: var(--interactive-hover);
color: var(--text-normal);
}
/* Template Sections */
@ -58,8 +58,9 @@
border-bottom: 2px solid var(--background-modifier-border);
}
.template-section-title svg {
.template-section-title .lucide {
opacity: 0.8;
color: var(--text-muted);
}
.template-list {
@ -82,12 +83,20 @@
.template-item:hover {
background: var(--background-modifier-hover);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
border-color: var(--background-modifier-border-hover);
box-shadow: 0 2px 8px var(--shadow-color);
}
.template-item.enabled {
background: var(--background-modifier-success);
border-color: var(--interactive-success);
background: var(--background-modifier-hover);
border-color: var(--interactive-accent);
box-shadow: 0 0 0 1px var(--interactive-accent);
}
.template-item.enabled:hover {
box-shadow:
0 2px 8px var(--shadow-color),
0 0 0 1px var(--interactive-accent);
}
.template-item-content {
@ -109,6 +118,9 @@
}
.template-custom-badge {
display: flex;
align-items: center;
gap: var(--size-2-1);
padding: var(--size-2-1) var(--size-2-2);
background: var(--interactive-accent);
color: var(--text-on-accent);
@ -119,6 +131,11 @@
letter-spacing: 0.05em;
}
.template-custom-badge .lucide {
width: 12px;
height: 12px;
}
/* Template Item Actions */
.template-item-actions {
display: flex;
@ -143,6 +160,7 @@
cursor: pointer;
transition: all var(--anim-duration-fast) ease;
color: var(--text-muted);
background: transparent;
}
.template-action-btn:hover {
@ -150,8 +168,8 @@
}
.template-action-btn--edit {
background: var(--background-modifier-border-hover);
color: var(--text-accent);
background: var(--interactive-normal);
color: var(--text-normal);
}
.template-action-btn--edit:hover {
@ -160,7 +178,7 @@
}
.template-action-btn--delete {
background: var(--background-modifier-error);
background: var(--interactive-normal);
color: var(--text-error);
}
@ -169,6 +187,11 @@
color: var(--text-on-accent);
}
.template-action-btn .lucide {
width: 16px;
height: 16px;
}
/* Template Editor Modal */
.template-editor-modal {
background: var(--background-primary);
@ -206,10 +229,18 @@
}
.template-editor-add-status {
display: flex;
align-items: center;
gap: var(--size-2-2);
align-self: flex-start;
margin-top: var(--size-4-2);
}
.template-editor-add-status .lucide {
width: 16px;
height: 16px;
}
.template-editor-modal__actions {
display: flex;
gap: var(--size-4-2);
@ -227,16 +258,16 @@
font-weight: var(--font-medium);
cursor: pointer;
transition: all var(--anim-duration-fast) ease;
border: 1px solid var(--background-modifier-border);
}
.template-editor-modal__actions button:not(.mod-cta) {
background: var(--background-modifier-border);
border: 1px solid var(--background-modifier-border-hover);
background: var(--interactive-normal);
color: var(--text-normal);
}
.template-editor-modal__actions button:not(.mod-cta):hover {
background: var(--background-modifier-border-hover);
background: var(--interactive-hover);
}
.template-editor-modal__actions .mod-cta:disabled {