fix: category handling
This commit is contained in:
@@ -359,6 +359,78 @@
|
||||
background-color: var(--vscode-button-secondaryHoverBackground);
|
||||
}
|
||||
|
||||
/* Categories management styles */
|
||||
.categories-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.category-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 10px;
|
||||
background-color: var(--vscode-badge-background);
|
||||
color: var(--vscode-badge-foreground);
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.category-name {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.category-remove {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--vscode-badge-foreground);
|
||||
cursor: pointer;
|
||||
opacity: 0.6;
|
||||
font-size: 10px;
|
||||
border-radius: 50%;
|
||||
transition: opacity 0.15s, background-color 0.15s;
|
||||
}
|
||||
|
||||
.category-remove:hover {
|
||||
opacity: 1;
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.category-add-form {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
padding: 12px 16px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.category-add-form input {
|
||||
flex: 1;
|
||||
max-width: 300px;
|
||||
padding: 6px 12px;
|
||||
font-size: 13px;
|
||||
background-color: var(--vscode-input-background);
|
||||
border: 1px solid var(--vscode-input-border);
|
||||
color: var(--vscode-input-foreground);
|
||||
border-radius: 4px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.category-add-form input:focus {
|
||||
border-color: var(--vscode-focusBorder);
|
||||
}
|
||||
|
||||
.category-add-form input::placeholder {
|
||||
color: var(--vscode-input-placeholderForeground);
|
||||
}
|
||||
|
||||
/* Responsive - narrow sidebar */
|
||||
@media (max-width: 600px) {
|
||||
.settings-nav {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { showToast } from '../Toast';
|
||||
import './SettingsView.css';
|
||||
|
||||
// Settings categories matching VS Code style
|
||||
type SettingsCategory = 'editor' | 'sync' | 'publishing' | 'data';
|
||||
type SettingsCategory = 'editor' | 'content' | 'sync' | 'publishing' | 'data';
|
||||
|
||||
interface Credentials {
|
||||
// Turso Cloud Sync
|
||||
@@ -45,9 +45,13 @@ const SearchIcon = () => (
|
||||
</svg>
|
||||
);
|
||||
|
||||
// Default post categories based on VISION.md
|
||||
const DEFAULT_POST_CATEGORIES = ['article', 'picture', 'aside', 'page'];
|
||||
|
||||
// Category definitions
|
||||
const categories: { id: SettingsCategory; label: string; icon: string }[] = [
|
||||
{ id: 'editor', label: 'Editor', icon: '📝' },
|
||||
{ id: 'content', label: 'Content', icon: '📋' },
|
||||
{ id: 'sync', label: 'Sync', icon: '🔄' },
|
||||
{ id: 'publishing', label: 'Publishing', icon: '🚀' },
|
||||
{ id: 'data', label: 'Data Management', icon: '🗄️' },
|
||||
@@ -96,16 +100,29 @@ export const SettingsView: React.FC = () => {
|
||||
const [showSecrets, setShowSecrets] = useState(false);
|
||||
const [dropboxConfigured, setDropboxConfigured] = useState(false);
|
||||
const [dropboxLastSync, setDropboxLastSync] = useState<string | null>(null);
|
||||
|
||||
// Post categories management
|
||||
const [postCategories, setPostCategories] = useState<string[]>(DEFAULT_POST_CATEGORIES);
|
||||
const [newCategoryInput, setNewCategoryInput] = useState('');
|
||||
|
||||
// Load saved credentials
|
||||
// Load saved credentials and categories
|
||||
useEffect(() => {
|
||||
const loadCredentials = async () => {
|
||||
const loadSettings = async () => {
|
||||
try {
|
||||
const savedCreds = localStorage.getItem('bds-credentials');
|
||||
if (savedCreds) {
|
||||
setCredentials({ ...defaultCredentials, ...JSON.parse(savedCreds) });
|
||||
}
|
||||
|
||||
// Load saved post categories
|
||||
const savedCategories = localStorage.getItem('bds-categories');
|
||||
if (savedCategories) {
|
||||
const categories = JSON.parse(savedCategories);
|
||||
if (Array.isArray(categories) && categories.length > 0) {
|
||||
setPostCategories(categories);
|
||||
}
|
||||
}
|
||||
|
||||
// Check Dropbox status
|
||||
const dbxConfigured = await window.electronAPI?.dropbox?.isConfigured();
|
||||
setDropboxConfigured(dbxConfigured || false);
|
||||
@@ -115,10 +132,10 @@ export const SettingsView: React.FC = () => {
|
||||
setDropboxLastSync(lastSync || null);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load credentials:', error);
|
||||
console.error('Failed to load settings:', error);
|
||||
}
|
||||
};
|
||||
loadCredentials();
|
||||
loadSettings();
|
||||
}, []);
|
||||
|
||||
// Save credentials and configure backends
|
||||
@@ -274,6 +291,85 @@ export const SettingsView: React.FC = () => {
|
||||
</>
|
||||
);
|
||||
|
||||
// Handlers for post categories management
|
||||
const handleAddCategory = () => {
|
||||
const trimmed = newCategoryInput.trim().toLowerCase();
|
||||
if (trimmed && !postCategories.includes(trimmed)) {
|
||||
const updated = [...postCategories, trimmed];
|
||||
setPostCategories(updated);
|
||||
localStorage.setItem('bds-categories', JSON.stringify(updated));
|
||||
setNewCategoryInput('');
|
||||
showToast.success(`Category "${trimmed}" added`);
|
||||
} else if (postCategories.includes(trimmed)) {
|
||||
showToast.error('Category already exists');
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveCategory = (categoryToRemove: string) => {
|
||||
if (postCategories.length <= 1) {
|
||||
showToast.error('Must have at least one category');
|
||||
return;
|
||||
}
|
||||
const updated = postCategories.filter(c => c !== categoryToRemove);
|
||||
setPostCategories(updated);
|
||||
localStorage.setItem('bds-categories', JSON.stringify(updated));
|
||||
showToast.success(`Category "${categoryToRemove}" removed`);
|
||||
};
|
||||
|
||||
const handleResetCategories = () => {
|
||||
setPostCategories(DEFAULT_POST_CATEGORIES);
|
||||
localStorage.setItem('bds-categories', JSON.stringify(DEFAULT_POST_CATEGORIES));
|
||||
showToast.success('Categories reset to defaults');
|
||||
};
|
||||
|
||||
const renderContentSettings = () => (
|
||||
<>
|
||||
<SettingSection
|
||||
title="Post Categories"
|
||||
description="Manage the available categories for blog posts. Each post can have one category that determines its display template."
|
||||
>
|
||||
<div className="categories-list">
|
||||
{postCategories.map((cat) => (
|
||||
<div key={cat} className="category-item">
|
||||
<span className="category-name">{cat}</span>
|
||||
<button
|
||||
className="category-remove"
|
||||
onClick={() => handleRemoveCategory(cat)}
|
||||
title={`Remove "${cat}" category`}
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="category-add-form">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="New category name..."
|
||||
value={newCategoryInput}
|
||||
onChange={(e) => setNewCategoryInput(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
handleAddCategory();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<button className="primary" onClick={handleAddCategory}>
|
||||
Add Category
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="setting-actions">
|
||||
<button className="secondary" onClick={handleResetCategories}>
|
||||
Reset to Defaults
|
||||
</button>
|
||||
</div>
|
||||
</SettingSection>
|
||||
</>
|
||||
);
|
||||
|
||||
const renderSyncSettings = () => (
|
||||
<>
|
||||
<SettingSection
|
||||
@@ -647,6 +743,7 @@ export const SettingsView: React.FC = () => {
|
||||
return (
|
||||
<>
|
||||
{renderEditorSettings()}
|
||||
{renderContentSettings()}
|
||||
{renderSyncSettings()}
|
||||
{renderPublishingSettings()}
|
||||
{renderDataSettings()}
|
||||
@@ -657,6 +754,8 @@ export const SettingsView: React.FC = () => {
|
||||
switch (activeCategory) {
|
||||
case 'editor':
|
||||
return renderEditorSettings();
|
||||
case 'content':
|
||||
return renderContentSettings();
|
||||
case 'sync':
|
||||
return renderSyncSettings();
|
||||
case 'publishing':
|
||||
|
||||
Reference in New Issue
Block a user