Files
stack/apps/web/src/components/knowledge/EntryMetadata.tsx
Jason Woltje ac1f2c176f
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
fix: Resolve all ESLint errors and warnings in web package
Fixes all 542 ESLint problems in the web package to achieve 0 errors and 0 warnings.

Changes:
- Fixed 144 issues: nullish coalescing, return types, unused variables
- Fixed 118 issues: unnecessary conditions, type safety, template literals
- Fixed 79 issues: non-null assertions, unsafe assignments, empty functions
- Fixed 67 issues: explicit return types, promise handling, enum comparisons
- Fixed 45 final warnings: missing return types, optional chains
- Fixed 25 typecheck-related issues: async/await, type assertions, formatting
- Fixed JSX.Element namespace errors across 90+ files

All Quality Rails violations resolved. Lint and typecheck both pass with 0 problems.

Files modified: 118 components, tests, hooks, and utilities

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-31 00:10:03 -06:00

148 lines
4.8 KiB
TypeScript

"use client";
import React from "react";
import type { KnowledgeTag } from "@mosaic/shared";
import { EntryStatus, Visibility } from "@mosaic/shared";
interface EntryMetadataProps {
title: string;
status: EntryStatus;
visibility: Visibility;
selectedTags: string[];
availableTags: KnowledgeTag[];
onTitleChange: (title: string) => void;
onStatusChange: (status: EntryStatus) => void;
onVisibilityChange: (visibility: Visibility) => void;
onTagsChange: (tags: string[]) => void;
}
/**
* EntryMetadata - Title, tags, status, and visibility controls
*/
export function EntryMetadata({
title,
status,
visibility,
selectedTags,
availableTags,
onTitleChange,
onStatusChange,
onVisibilityChange,
onTagsChange,
}: EntryMetadataProps): React.JSX.Element {
const handleTagToggle = (tagId: string): void => {
if (selectedTags.includes(tagId)) {
onTagsChange(selectedTags.filter((id) => id !== tagId));
} else {
onTagsChange([...selectedTags, tagId]);
}
};
return (
<div className="entry-metadata space-y-4">
{/* Title */}
<div>
<label
htmlFor="entry-title"
className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
>
Title
</label>
<input
id="entry-title"
type="text"
value={title}
onChange={(e) => {
onTitleChange(e.target.value);
}}
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-700 rounded-md bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-blue-500 focus:border-transparent"
placeholder="Entry title..."
required
/>
</div>
{/* Status and Visibility Row */}
<div className="grid grid-cols-2 gap-4">
{/* Status */}
<div>
<label
htmlFor="entry-status"
className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
>
Status
</label>
<select
id="entry-status"
value={status}
onChange={(e) => {
onStatusChange(e.target.value as EntryStatus);
}}
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-700 rounded-md bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-blue-500 focus:border-transparent"
>
<option value={EntryStatus.DRAFT}>Draft</option>
<option value={EntryStatus.PUBLISHED}>Published</option>
<option value={EntryStatus.ARCHIVED}>Archived</option>
</select>
</div>
{/* Visibility */}
<div>
<label
htmlFor="entry-visibility"
className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
>
Visibility
</label>
<select
id="entry-visibility"
value={visibility}
onChange={(e) => {
onVisibilityChange(e.target.value as Visibility);
}}
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-700 rounded-md bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-blue-500 focus:border-transparent"
>
<option value={Visibility.PRIVATE}>Private</option>
<option value={Visibility.WORKSPACE}>Workspace</option>
<option value={Visibility.PUBLIC}>Public</option>
</select>
</div>
</div>
{/* Tags */}
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Tags
</label>
<div className="flex flex-wrap gap-2">
{availableTags.length > 0 ? (
availableTags.map((tag) => {
const isSelected = selectedTags.includes(tag.id);
return (
<button
key={tag.id}
type="button"
onClick={() => {
handleTagToggle(tag.id);
}}
className={`px-3 py-1 rounded-full text-sm font-medium transition-colors ${
isSelected
? "bg-blue-600 text-white"
: "bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:bg-gray-300 dark:hover:bg-gray-600"
}`}
style={isSelected && tag.color ? { backgroundColor: tag.color } : undefined}
>
{tag.name}
</button>
);
})
) : (
<p className="text-sm text-gray-500 dark:text-gray-400">
No tags available. Create tags first.
</p>
)}
</div>
</div>
</div>
);
}