-
This commit is contained in:
@@ -1,203 +1,225 @@
|
||||
import { Theme, SpacingSystem, Typography } from "../theme/theme.slint";
|
||||
import { Card } from "../components/card.slint";
|
||||
// Profiles Page - Configuration profiles management
|
||||
import { Theme, Typography, SpacingSystem } from "../theme/theme.slint";
|
||||
import { Card, CardHeader, CardTitle, CardContent, CardFooter } from "../components/card.slint";
|
||||
import { Button } from "../components/button.slint";
|
||||
import { Badge } from "../components/badge.slint";
|
||||
import { Progress } from "../components/progress.slint";
|
||||
import { Empty } from "../components/empty.slint";
|
||||
import { Profile } from "../types.slint";
|
||||
|
||||
export struct ProfileData {
|
||||
id: string,
|
||||
name: string,
|
||||
type: string, // "local" or "remote"
|
||||
description: string,
|
||||
usage: float, // For remote profiles
|
||||
total: float, // For remote profiles
|
||||
updated: string,
|
||||
}
|
||||
export component ProfilesPage inherits Rectangle {
|
||||
in property <[Profile]> profiles;
|
||||
|
||||
export component ProfilesPage {
|
||||
in property <[ProfileData]> profiles: [];
|
||||
in property <string> selected-profile-id: "";
|
||||
|
||||
callback load-profiles();
|
||||
callback select-profile(string /* profile-id */);
|
||||
callback update-profile(string /* profile-id */);
|
||||
callback delete-profile(string /* profile-id */);
|
||||
callback add-profile();
|
||||
callback refresh-all();
|
||||
callback select-profile(string);
|
||||
callback edit-profile(string);
|
||||
callback delete-profile(string);
|
||||
callback refresh-profile(string);
|
||||
|
||||
VerticalLayout {
|
||||
padding: SpacingSystem.spacing.s4;
|
||||
spacing: SpacingSystem.spacing.s4;
|
||||
|
||||
// Header
|
||||
HorizontalLayout {
|
||||
spacing: SpacingSystem.spacing.s3;
|
||||
alignment: space-between;
|
||||
|
||||
Text {
|
||||
text: "Profiles";
|
||||
color: Theme.colors.foreground;
|
||||
font-size: Typography.sizes.xl;
|
||||
font-weight: Typography.weights.bold;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
|
||||
HorizontalLayout {
|
||||
spacing: SpacingSystem.spacing.s2;
|
||||
|
||||
Button {
|
||||
text: "+";
|
||||
variant: "ghost";
|
||||
|
||||
clicked => {
|
||||
root.add-profile();
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "↻";
|
||||
variant: "ghost";
|
||||
|
||||
clicked => {
|
||||
root.refresh-all();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Profile Grid
|
||||
ScrollView {
|
||||
GridLayout {
|
||||
spacing: SpacingSystem.spacing.s3;
|
||||
|
||||
for profile in root.profiles: ProfileItem {
|
||||
profile-data: profile;
|
||||
selected: profile.id == root.selected-profile-id;
|
||||
|
||||
clicked => {
|
||||
root.select-profile(profile.id);
|
||||
}
|
||||
|
||||
refresh-clicked => {
|
||||
root.refresh-profile(profile.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component ProfileItem {
|
||||
in property <ProfileData> profile-data;
|
||||
in property <bool> selected: false;
|
||||
|
||||
callback clicked();
|
||||
callback refresh-clicked();
|
||||
|
||||
private property <bool> hovered: false;
|
||||
|
||||
min-width: 288px; // 18rem
|
||||
height: 96px;
|
||||
|
||||
states [
|
||||
selected when root.selected: {
|
||||
container.border-color: Theme.colors.primary.transparentize(0.5);
|
||||
container.background: Theme.colors.accent.transparentize(0.4);
|
||||
}
|
||||
]
|
||||
|
||||
container := Card {
|
||||
border-width: 2px;
|
||||
|
||||
VerticalLa spacing: SpacingSystem.spacing.s2;
|
||||
|
||||
// Header
|
||||
background: Theme.colors.background;
|
||||
|
||||
VerticalLayout {
|
||||
// Fixed header
|
||||
Rectangle {
|
||||
height: 48px;
|
||||
background: Theme.colors.background;
|
||||
|
||||
HorizontalLayout {
|
||||
spacing: SpacingSystem.spacing.s2;
|
||||
padding: SpacingSystem.spacing.s4;
|
||||
spacing: SpacingSystem.spacing.s4;
|
||||
alignment: space-between;
|
||||
|
||||
|
||||
Text {
|
||||
text: "Profiles";
|
||||
font-size: Typography.sizes.xl;
|
||||
font-weight: Typography.weights.bold;
|
||||
color: Theme.colors.foreground;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
|
||||
// Action buttons
|
||||
HorizontalLayout {
|
||||
spacing: SpacingSystem.spacing.s2;
|
||||
|
||||
Text {
|
||||
text: root.profile-data.type == "remote" ? "☁" : "📄";
|
||||
font-size: Typography.sizes.lg;
|
||||
vertical-alignment: center;
|
||||
|
||||
Button {
|
||||
text: "➕";
|
||||
variant: "ghost";
|
||||
size: "sm";
|
||||
|
||||
clicked => {
|
||||
root.add-profile();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
text: root.profile-data.name;
|
||||
color: Theme.colors.foreground;
|
||||
font-size: Typography.sizes.base;
|
||||
font-weight: Typography.weights.semibold;
|
||||
vertical-alignment: center;
|
||||
|
||||
Button {
|
||||
text: "🔄";
|
||||
variant: "ghost";
|
||||
size: "sm";
|
||||
|
||||
clicked => {
|
||||
root.load-profiles();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if root.profile-data.type == "remote": Button {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
text: "↻";
|
||||
variant: "ghost";
|
||||
|
||||
clicked => {
|
||||
root.refresh-clicked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Description
|
||||
Text {
|
||||
text: root.profile-data.description;
|
||||
color: Theme.colors.muted-foreground;
|
||||
font-size: Typography.sizes.xs;
|
||||
overflow: elide;
|
||||
}
|
||||
|
||||
// Progress bar for remote profiles
|
||||
if root.profile-data.type == "remote": VerticalLayout {
|
||||
spacing: SpacingSystem.spacing.s1;
|
||||
|
||||
Progress {
|
||||
value: (root.profile-data.usage / root.profile-data.total) * 100;
|
||||
}
|
||||
|
||||
HorizontalLayout {
|
||||
alignment: space-between;
|
||||
|
||||
Text {
|
||||
text: "Updated: " + root.profile-data.updated;
|
||||
color: Theme.colors.muted-foreground;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
Text {
|
||||
text: root.profile-data.usage + " / " + root.profile-data.total + " GB";
|
||||
color: Theme.colors.oreground;
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Local profile info
|
||||
if root.profile-data.type == "local": HorizontalLayout {
|
||||
alignment: space-between;
|
||||
|
||||
Text {
|
||||
text: "Local Profile";
|
||||
color: Theme.colors.muted-foreground;
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
touch := TouchArea {
|
||||
clicked => {
|
||||
root.clicked();
|
||||
}
|
||||
|
||||
moved => {
|
||||
root.hovered = self.has-hover;
|
||||
|
||||
// Scrollable content
|
||||
Flickable {
|
||||
viewport-height: content-layout.preferred-height + SpacingSystem.spacing.s4 * 2;
|
||||
|
||||
content-layout := VerticalLayout {
|
||||
padding: SpacingSystem.spacing.s4;
|
||||
spacing: SpacingSystem.spacing.s4;
|
||||
|
||||
// Empty state
|
||||
if profiles.length == 0: Empty {
|
||||
height: 300px;
|
||||
icon: "📋";
|
||||
title: "No Profiles";
|
||||
description: "Click + to add a new profile";
|
||||
}
|
||||
|
||||
// Profile list
|
||||
for profile in profiles: Card {
|
||||
hoverable: true;
|
||||
|
||||
CardHeader {
|
||||
HorizontalLayout {
|
||||
spacing: SpacingSystem.spacing.s2;
|
||||
alignment: space-between;
|
||||
|
||||
HorizontalLayout {
|
||||
spacing: SpacingSystem.spacing.s2;
|
||||
|
||||
Text {
|
||||
text: "☁";
|
||||
font-size: Typography.sizes.xl;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
|
||||
VerticalLayout {
|
||||
spacing: SpacingSystem.spacing.s1;
|
||||
|
||||
CardTitle {
|
||||
text: profile.name;
|
||||
}
|
||||
|
||||
HorizontalLayout {
|
||||
spacing: SpacingSystem.spacing.s1;
|
||||
|
||||
Badge {
|
||||
text: profile.type;
|
||||
variant: "outline";
|
||||
}
|
||||
|
||||
if profile.selected: Badge {
|
||||
text: "Active";
|
||||
variant: "default";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "🔄";
|
||||
variant: "ghost";
|
||||
size: "sm";
|
||||
|
||||
clicked => {
|
||||
root.update-profile(profile.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CardContent {
|
||||
VerticalLayout {
|
||||
spacing: SpacingSystem.spacing.s2;
|
||||
|
||||
// Usage
|
||||
HorizontalLayout {
|
||||
spacing: SpacingSystem.spacing.s2;
|
||||
alignment: space-between;
|
||||
|
||||
Text {
|
||||
text: "Usage:";
|
||||
font-size: Typography.sizes.xs;
|
||||
color: Theme.colors.muted-foreground;
|
||||
}
|
||||
|
||||
Text {
|
||||
text: profile.usage;
|
||||
font-size: Typography.sizes.xs;
|
||||
color: Theme.colors.foreground;
|
||||
font-weight: Typography.weights.medium;
|
||||
}
|
||||
}
|
||||
|
||||
// Expire date
|
||||
HorizontalLayout {
|
||||
spacing: SpacingSystem.spacing.s2;
|
||||
alignment: space-between;
|
||||
|
||||
Text {
|
||||
text: "Expires:";
|
||||
font-size: Typography.sizes.xs;
|
||||
color: Theme.colors.muted-foreground;
|
||||
}
|
||||
|
||||
Text {
|
||||
text: profile.expire-at;
|
||||
font-size: Typography.sizes.xs;
|
||||
color: Theme.colors.foreground;
|
||||
font-weight: Typography.weights.medium;
|
||||
}
|
||||
}
|
||||
|
||||
// Updated date
|
||||
HorizontalLayout {
|
||||
spacing: SpacingSystem.spacing.s2;
|
||||
alignment: space-between;
|
||||
|
||||
Text {
|
||||
text: "Updated:";
|
||||
font-size: Typography.sizes.xs;
|
||||
color: Theme.colors.muted-foreground;
|
||||
}
|
||||
|
||||
Text {
|
||||
text: profile.updated-at;
|
||||
font-size: Typography.sizes.xs;
|
||||
color: Theme.colors.muted-foreground;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CardFooter {
|
||||
HorizontalLayout {
|
||||
spacing: SpacingSystem.spacing.s2;
|
||||
|
||||
if !profile.selected: Button {
|
||||
text: "Select";
|
||||
variant: "default";
|
||||
size: "sm";
|
||||
|
||||
clicked => {
|
||||
root.select-profile(profile.id);
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Delete";
|
||||
variant: "destructive";
|
||||
size: "sm";
|
||||
|
||||
clicked => {
|
||||
root.delete-profile(profile.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user