mirror of
https://github.com/kjanat/livedash-node.git
synced 2026-01-16 11:32:13 +01:00
feat: comprehensive UI improvements and new logo design
- Replace old logo with modern dashboard tiles design - Improve text selection styling using Tailwind selection variant - Fix session ID display with proper truncation classes - Clean up temporary logo files and showcase page - Enhance dark mode support across company settings and sessions pages - Remove obsolete Alert Configuration from company settings - Add collapsible filters and mobile-optimized view details buttons
This commit is contained in:
@ -8,7 +8,7 @@ import { Button } from "@/components/ui/button";
|
|||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Alert, AlertDescription } from "@/components/ui/alert";
|
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||||
import { ShieldX, Settings, Save, Database, Bell } from "lucide-react";
|
import { ShieldX, Settings, Save, Database } from "lucide-react";
|
||||||
|
|
||||||
export default function CompanySettingsPage() {
|
export default function CompanySettingsPage() {
|
||||||
const { data: session, status } = useSession();
|
const { data: session, status } = useSession();
|
||||||
@ -18,7 +18,6 @@ export default function CompanySettingsPage() {
|
|||||||
const [csvUrl, setCsvUrl] = useState<string>("");
|
const [csvUrl, setCsvUrl] = useState<string>("");
|
||||||
const [csvUsername, setCsvUsername] = useState<string>("");
|
const [csvUsername, setCsvUsername] = useState<string>("");
|
||||||
const [csvPassword, setCsvPassword] = useState<string>("");
|
const [csvPassword, setCsvPassword] = useState<string>("");
|
||||||
const [sentimentThreshold, setSentimentThreshold] = useState<string>("");
|
|
||||||
const [message, setMessage] = useState<string>("");
|
const [message, setMessage] = useState<string>("");
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
@ -32,7 +31,6 @@ export default function CompanySettingsPage() {
|
|||||||
setCompany(data.company);
|
setCompany(data.company);
|
||||||
setCsvUrl(data.company.csvUrl || "");
|
setCsvUrl(data.company.csvUrl || "");
|
||||||
setCsvUsername(data.company.csvUsername || "");
|
setCsvUsername(data.company.csvUsername || "");
|
||||||
setSentimentThreshold(data.company.sentimentAlert?.toString() || "");
|
|
||||||
if (data.company.csvPassword) {
|
if (data.company.csvPassword) {
|
||||||
setCsvPassword(data.company.csvPassword);
|
setCsvPassword(data.company.csvPassword);
|
||||||
}
|
}
|
||||||
@ -57,7 +55,6 @@ export default function CompanySettingsPage() {
|
|||||||
csvUrl,
|
csvUrl,
|
||||||
csvUsername,
|
csvUsername,
|
||||||
csvPassword,
|
csvPassword,
|
||||||
sentimentThreshold,
|
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -146,85 +143,54 @@ export default function CompanySettingsPage() {
|
|||||||
}}
|
}}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
>
|
>
|
||||||
<div className="grid gap-6 md:grid-cols-2">
|
<Card>
|
||||||
<Card>
|
<CardHeader>
|
||||||
<CardHeader>
|
<div className="flex items-center gap-2">
|
||||||
<div className="flex items-center gap-2">
|
<Database className="h-5 w-5" />
|
||||||
<Database className="h-5 w-5" />
|
<CardTitle className="text-lg">Data Source Configuration</CardTitle>
|
||||||
<CardTitle className="text-lg">Data Source Configuration</CardTitle>
|
</div>
|
||||||
</div>
|
</CardHeader>
|
||||||
</CardHeader>
|
<CardContent className="space-y-4">
|
||||||
<CardContent className="space-y-4">
|
<div className="space-y-2">
|
||||||
<div className="space-y-2">
|
<Label htmlFor="csvUrl">CSV Data Source URL</Label>
|
||||||
<Label htmlFor="csvUrl">CSV Data Source URL</Label>
|
<Input
|
||||||
<Input
|
id="csvUrl"
|
||||||
id="csvUrl"
|
type="text"
|
||||||
type="text"
|
value={csvUrl}
|
||||||
value={csvUrl}
|
onChange={(e) => setCsvUrl(e.target.value)}
|
||||||
onChange={(e) => setCsvUrl(e.target.value)}
|
placeholder="https://example.com/data.csv"
|
||||||
placeholder="https://example.com/data.csv"
|
autoComplete="off"
|
||||||
autoComplete="off"
|
/>
|
||||||
/>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="csvUsername">CSV Username</Label>
|
<Label htmlFor="csvUsername">CSV Username</Label>
|
||||||
<Input
|
<Input
|
||||||
id="csvUsername"
|
id="csvUsername"
|
||||||
type="text"
|
type="text"
|
||||||
value={csvUsername}
|
value={csvUsername}
|
||||||
onChange={(e) => setCsvUsername(e.target.value)}
|
onChange={(e) => setCsvUsername(e.target.value)}
|
||||||
placeholder="Username for CSV access (if needed)"
|
placeholder="Username for CSV access (if needed)"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="csvPassword">CSV Password</Label>
|
<Label htmlFor="csvPassword">CSV Password</Label>
|
||||||
<Input
|
<Input
|
||||||
id="csvPassword"
|
id="csvPassword"
|
||||||
type="password"
|
type="password"
|
||||||
value={csvPassword}
|
value={csvPassword}
|
||||||
onChange={(e) => setCsvPassword(e.target.value)}
|
onChange={(e) => setCsvPassword(e.target.value)}
|
||||||
placeholder="Password will be updated only if provided"
|
placeholder="Password will be updated only if provided"
|
||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
/>
|
/>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
Leave blank to keep current password
|
Leave blank to keep current password
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Bell className="h-5 w-5" />
|
|
||||||
<CardTitle className="text-lg">Alert Configuration</CardTitle>
|
|
||||||
</div>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label htmlFor="sentimentThreshold">
|
|
||||||
Sentiment Alert Threshold
|
|
||||||
</Label>
|
|
||||||
<Input
|
|
||||||
id="sentimentThreshold"
|
|
||||||
type="number"
|
|
||||||
value={sentimentThreshold}
|
|
||||||
onChange={(e) => setSentimentThreshold(e.target.value)}
|
|
||||||
placeholder="Threshold value (0-100)"
|
|
||||||
min="0"
|
|
||||||
max="100"
|
|
||||||
autoComplete="off"
|
|
||||||
/>
|
|
||||||
<p className="text-sm text-muted-foreground">
|
|
||||||
Percentage of negative sentiment sessions to trigger alert (0-100)
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button type="submit" className="gap-2">
|
<Button type="submit" className="gap-2">
|
||||||
|
|||||||
@ -339,19 +339,24 @@ export default function SessionsPage() {
|
|||||||
<Badge variant="outline" className="font-mono text-xs">
|
<Badge variant="outline" className="font-mono text-xs">
|
||||||
ID
|
ID
|
||||||
</Badge>
|
</Badge>
|
||||||
<code className="text-sm text-muted-foreground font-mono">
|
<code className="text-sm text-muted-foreground font-mono truncate max-w-24">
|
||||||
{(session.sessionId || session.id).slice(0, 8)}...
|
{session.sessionId || session.id}
|
||||||
</code>
|
</code>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
<div className="flex items-center gap-2">
|
||||||
<Clock className="h-4 w-4" />
|
<Badge variant="outline" className="text-xs">
|
||||||
{new Date(session.startTime).toLocaleString()}
|
<Clock className="h-3 w-3 mr-1" />
|
||||||
|
{new Date(session.startTime).toLocaleDateString()}
|
||||||
|
</Badge>
|
||||||
|
<span className="text-xs text-muted-foreground">
|
||||||
|
{new Date(session.startTime).toLocaleTimeString()}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Link href={`/dashboard/sessions/${session.id}`}>
|
<Link href={`/dashboard/sessions/${session.id}`}>
|
||||||
<Button variant="outline" size="sm" className="gap-2">
|
<Button variant="outline" size="sm" className="gap-2">
|
||||||
<Eye className="h-4 w-4" />
|
<Eye className="h-4 w-4" />
|
||||||
View Details
|
<span className="hidden sm:inline">View Details</span>
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -163,23 +163,12 @@
|
|||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
* {
|
* {
|
||||||
@apply border-border outline-ring/50;
|
@apply border-border outline-ring/50 selection:bg-primary/30 selection:text-primary-foreground;
|
||||||
}
|
}
|
||||||
body {
|
body {
|
||||||
@apply bg-background text-foreground;
|
@apply bg-background text-foreground;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Custom text selection colors */
|
|
||||||
::selection {
|
|
||||||
background-color: hsl(var(--primary) / 0.3);
|
|
||||||
color: hsl(var(--primary-foreground));
|
|
||||||
}
|
|
||||||
|
|
||||||
::-moz-selection {
|
|
||||||
background-color: hsl(var(--primary) / 0.3);
|
|
||||||
color: hsl(var(--primary-foreground));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Line clamp utility */
|
/* Line clamp utility */
|
||||||
.line-clamp-2 {
|
.line-clamp-2 {
|
||||||
display: -webkit-box;
|
display: -webkit-box;
|
||||||
|
|||||||
@ -1,50 +1,36 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||||
<style>
|
<defs>
|
||||||
path,
|
<linearGradient id="primaryGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||||
circle {
|
<stop offset="0%" style="stop-color:#6366f1;stop-opacity:1" />
|
||||||
stroke-width: 1.5;
|
<stop offset="100%" style="stop-color:#3730a3;stop-opacity:1" />
|
||||||
}
|
</linearGradient>
|
||||||
|
<linearGradient id="accentGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||||
|
<stop offset="0%" style="stop-color:#10b981;stop-opacity:1" />
|
||||||
|
<stop offset="100%" style="stop-color:#059669;stop-opacity:1" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
|
||||||
.bg {
|
<!-- Background -->
|
||||||
fill: #ffffff;
|
<rect x="2" y="2" width="28" height="28" rx="6" fill="url(#primaryGradient)" />
|
||||||
stroke: #3b82f6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
<!-- Dashboard tiles grid -->
|
||||||
fill: #3b82f6;
|
<!-- Top row -->
|
||||||
stroke: #3b82f6;
|
<rect x="6" y="7" width="6" height="6" rx="2" fill="white" opacity="0.9" />
|
||||||
}
|
<rect x="14" y="7" width="6" height="6" rx="2" fill="url(#accentGradient)" />
|
||||||
|
<rect x="22" y="7" width="4" height="6" rx="2" fill="white" opacity="0.7" />
|
||||||
|
|
||||||
.text {
|
<!-- Middle row -->
|
||||||
fill: #3b82f6;
|
<rect x="6" y="15" width="4" height="4" rx="1.5" fill="white" opacity="0.7" />
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
<rect x="12" y="15" width="8" height="4" rx="1.5" fill="white" opacity="0.8" />
|
||||||
font-weight: bold;
|
<rect x="22" y="15" width="4" height="4" rx="1.5" fill="white" opacity="0.9" />
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
<!-- Bottom row -->
|
||||||
.bg {
|
<rect x="6" y="21" width="6" height="4" rx="1.5" fill="white" opacity="0.8" />
|
||||||
fill: #1e3a8a;
|
<rect x="14" y="21" width="4" height="4" rx="1.5" fill="white" opacity="0.7" />
|
||||||
stroke: #93c5fd;
|
<rect x="20" y="21" width="6" height="4" rx="1.5" fill="white" opacity="0.9" />
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
<!-- Small data dots inside tiles -->
|
||||||
fill: #93c5fd;
|
<circle cx="9" cy="10" r="1" fill="url(#primaryGradient)" opacity="0.8" />
|
||||||
stroke: #93c5fd;
|
<circle cx="17" cy="10" r="1" fill="white" opacity="0.9" />
|
||||||
}
|
<circle cx="9" cy="23" r="0.8" fill="url(#accentGradient)" opacity="0.8" />
|
||||||
|
|
||||||
.text {
|
|
||||||
fill: #93c5fd;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<!-- Background circle -->
|
|
||||||
<circle cx="16" cy="16" r="14" class="bg" />
|
|
||||||
|
|
||||||
<!-- Dashboard chart-like icon -->
|
|
||||||
<path d="M10,22 L10,16 L13,16 L13,22 Z M15,22 L15,12 L18,12 L18,22 Z M20,22 L20,14 L23,14 L23,22 Z" class="icon" />
|
|
||||||
|
|
||||||
<!-- Text "LD" for LiveDash -->
|
|
||||||
<text x="9.5" y="10" class="text">LD</text>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.8 KiB |
Reference in New Issue
Block a user