4, partial 5

This commit is contained in:
Michael Freno
2026-02-04 01:00:57 -05:00
parent 7b5c256e07
commit d5ce8452e4
20 changed files with 2215 additions and 69 deletions

View File

@@ -0,0 +1,186 @@
/**
* Sync profile component for PodTUI
* Displays user profile information and sync status
*/
import { createSignal } from "solid-js"
import { useAuthStore } from "../stores/auth"
import { format } from "date-fns"
interface SyncProfileProps {
focused?: boolean
onLogout?: () => void
onManageSync?: () => void
}
type FocusField = "sync" | "export" | "logout"
export function SyncProfile(props: SyncProfileProps) {
const auth = useAuthStore()
const [focusField, setFocusField] = createSignal<FocusField>("sync")
const [lastSyncTime] = createSignal<Date | null>(new Date())
const fields: FocusField[] = ["sync", "export", "logout"]
const handleKeyPress = (key: { name: string; shift?: boolean }) => {
if (key.name === "tab") {
const currentIndex = fields.indexOf(focusField())
const nextIndex = key.shift
? (currentIndex - 1 + fields.length) % fields.length
: (currentIndex + 1) % fields.length
setFocusField(fields[nextIndex])
} else if (key.name === "return" || key.name === "enter") {
if (focusField() === "sync" && props.onManageSync) {
props.onManageSync()
} else if (focusField() === "logout" && props.onLogout) {
handleLogout()
}
}
}
const handleLogout = () => {
auth.logout()
if (props.onLogout) {
props.onLogout()
}
}
const formatDate = (date: Date | null | undefined): string => {
if (!date) return "Never"
return format(date, "MMM d, yyyy HH:mm")
}
const user = () => auth.state().user
// Get user initials for avatar
const userInitials = () => {
const name = user()?.name || "?"
return name.slice(0, 2).toUpperCase()
}
return (
<box
flexDirection="column"
border
padding={2}
gap={1}
onKeyPress={props.focused ? handleKeyPress : undefined}
>
<text>
<strong>User Profile</strong>
</text>
<box height={1} />
{/* User avatar and info */}
<box flexDirection="row" gap={2}>
{/* ASCII avatar */}
<box border padding={1} width={8} height={4} justifyContent="center" alignItems="center">
<text>
<span fg="cyan">{userInitials()}</span>
</text>
</box>
{/* User details */}
<box flexDirection="column" gap={0}>
<text>
<span fg="white">{user()?.name || "Guest User"}</span>
</text>
<text>
<span fg="gray">{user()?.email || "No email"}</span>
</text>
<text>
<span fg="gray">
Joined: {formatDate(user()?.createdAt)}
</span>
</text>
</box>
</box>
<box height={1} />
{/* Sync status section */}
<box border padding={1} flexDirection="column" gap={0}>
<text>
<span fg="cyan">Sync Status</span>
</text>
<box flexDirection="row" gap={1}>
<text>
<span fg="gray">Status:</span>
</text>
<text>
<span fg={user()?.syncEnabled ? "green" : "yellow"}>
{user()?.syncEnabled ? "Enabled" : "Disabled"}
</span>
</text>
</box>
<box flexDirection="row" gap={1}>
<text>
<span fg="gray">Last Sync:</span>
</text>
<text>
<span fg="white">{formatDate(lastSyncTime())}</span>
</text>
</box>
<box flexDirection="row" gap={1}>
<text>
<span fg="gray">Method:</span>
</text>
<text>
<span fg="white">File-based (JSON/XML)</span>
</text>
</box>
</box>
<box height={1} />
{/* Action buttons */}
<box flexDirection="row" gap={2}>
<box
border
padding={1}
backgroundColor={focusField() === "sync" ? "#333" : undefined}
>
<text>
<span fg={focusField() === "sync" ? "cyan" : undefined}>
[S] Manage Sync
</span>
</text>
</box>
<box
border
padding={1}
backgroundColor={focusField() === "export" ? "#333" : undefined}
>
<text>
<span fg={focusField() === "export" ? "cyan" : undefined}>
[E] Export Data
</span>
</text>
</box>
<box
border
padding={1}
backgroundColor={focusField() === "logout" ? "#333" : undefined}
>
<text>
<span fg={focusField() === "logout" ? "red" : "gray"}>
[L] Logout
</span>
</text>
</box>
</box>
<box height={1} />
<text>
<span fg="gray">Tab to navigate, Enter to select</span>
</text>
</box>
)
}