**Git Commit Tracking** - Automatically capture git commit hash when claims/observations are ingested - Store in assertion metadata for temporal context and audit trails - Graceful degradation in non-git environments - Solves double-commit problem by capturing hash at ingestion time **Implementation** - walker/git.rs: get_current_commit_hash() utility function - bridge.rs: Accept optional git_commit parameter in all conversion functions - episteme/local: Store project_root, capture git hash during ingestion - 5 new tests for git hash tracking + metadata validation - All 1162 aphoria tests passing **Documentation Overhaul** - README: Added Observations vs Claims distinction, git tracking, dashboard - CLI Reference: New sections for git integration and ignore/exclusion system - Comprehensive ignore documentation: .aphoriaignore, inline comments, 4 methods - Enhanced verification engine docs with matching capabilities - DOCUMENTATION_UPDATES.md: Complete audit summary **Dashboard Separation** - Moved Aphoria-specific UI from stemedb-dashboard to aphoria-dashboard - Clean separation of concerns: StemeDB for core, Aphoria for security - Added dashboard documentation and setup guides Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
89 lines
2.5 KiB
TypeScript
89 lines
2.5 KiB
TypeScript
"use client";
|
|
|
|
import { forwardRef } from "react";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
interface DatePickerProps {
|
|
value?: Date;
|
|
onChange: (date: Date | undefined) => void;
|
|
placeholder?: string;
|
|
disabled?: boolean;
|
|
className?: string;
|
|
max?: Date;
|
|
min?: Date;
|
|
}
|
|
|
|
export const DatePicker = forwardRef<HTMLInputElement, DatePickerProps>(
|
|
(
|
|
{
|
|
value,
|
|
onChange,
|
|
placeholder = "Select date...",
|
|
disabled = false,
|
|
className,
|
|
max,
|
|
min,
|
|
},
|
|
ref
|
|
) => {
|
|
// Convert Date to YYYY-MM-DD string for input value
|
|
const formatForInput = (date: Date | undefined): string => {
|
|
if (!date) return "";
|
|
const year = date.getFullYear();
|
|
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
const day = String(date.getDate()).padStart(2, "0");
|
|
return `${year}-${month}-${day}`;
|
|
};
|
|
|
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
const val = e.target.value;
|
|
if (!val) {
|
|
onChange(undefined);
|
|
return;
|
|
}
|
|
// Parse YYYY-MM-DD to Date
|
|
const [year, month, day] = val.split("-").map(Number);
|
|
const date = new Date(year, month - 1, day);
|
|
onChange(date);
|
|
};
|
|
|
|
const handleClear = () => {
|
|
onChange(undefined);
|
|
};
|
|
|
|
return (
|
|
<div className={cn("relative flex items-center gap-2", className)}>
|
|
<input
|
|
ref={ref}
|
|
type="date"
|
|
value={formatForInput(value)}
|
|
onChange={handleChange}
|
|
disabled={disabled}
|
|
max={max ? formatForInput(max) : undefined}
|
|
min={min ? formatForInput(min) : undefined}
|
|
placeholder={placeholder}
|
|
className={cn(
|
|
"flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm shadow-xs transition-colors",
|
|
"file:border-0 file:bg-transparent file:text-sm file:font-medium",
|
|
"placeholder:text-muted-foreground",
|
|
"focus-visible:outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
|
"disabled:cursor-not-allowed disabled:opacity-50"
|
|
)}
|
|
/>
|
|
{value && !disabled && (
|
|
<button
|
|
type="button"
|
|
onClick={handleClear}
|
|
className="absolute right-2 text-muted-foreground hover:text-foreground text-xs"
|
|
aria-label="Clear date"
|
|
>
|
|
Clear
|
|
</button>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
);
|
|
|
|
DatePicker.displayName = "DatePicker";
|