Custom welcome message (#234)
* json * connect * useWelcomMessage * Update shared/src/messages/jobs-welcome.json Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
co-authored by
Copilot
parent
7514aa1b28
commit
26dbed15b9
@@ -0,0 +1,57 @@
|
||||
import welcomeMessages from "@shared/messages/jobs-welcome.json";
|
||||
import { useMemo } from "react";
|
||||
import { useProfile } from "./useProfile";
|
||||
|
||||
function hashCode(str: string): number {
|
||||
let hash = 0;
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const char = str.charCodeAt(i);
|
||||
hash = (hash << 5) - hash + char;
|
||||
hash |= 0; // Convert to 32bit integer
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
export function useWelcomeMessage(): string {
|
||||
const { personName } = useProfile();
|
||||
|
||||
return useMemo(() => {
|
||||
const firstName = personName?.split(" ")[0] || "User";
|
||||
const today = new Date().toDateString();
|
||||
|
||||
let isFirstDay = true;
|
||||
try {
|
||||
let firstSeenDate = localStorage.getItem("jobOps_firstWelcomeDate");
|
||||
if (!firstSeenDate) {
|
||||
firstSeenDate = today;
|
||||
localStorage.setItem("jobOps_firstWelcomeDate", today);
|
||||
}
|
||||
isFirstDay = firstSeenDate === today;
|
||||
} catch (_e) {
|
||||
// Ignore localStorage errors (e.g. private mode restrictions)
|
||||
// Fallback to true so we just show the first message
|
||||
}
|
||||
|
||||
const lines = welcomeMessages.lines;
|
||||
let selectedIndex = 0; // Always default to the first message
|
||||
|
||||
if (!isFirstDay) {
|
||||
// If it's not their first day, randomize consistently for the day
|
||||
const seed = Math.abs(hashCode(`${firstName}-${today}`));
|
||||
selectedIndex = seed % lines.length;
|
||||
}
|
||||
|
||||
const line = lines[selectedIndex];
|
||||
|
||||
switch (line.placement) {
|
||||
case "inline":
|
||||
return line.text.replace("{name}", firstName);
|
||||
case "prefix":
|
||||
return `${firstName}, ${line.text}`;
|
||||
case "suffix":
|
||||
return `${line.text}, ${firstName}.`;
|
||||
default:
|
||||
return line.text;
|
||||
}
|
||||
}, [personName]);
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { PipelineProgress } from "@client/components";
|
||||
import { useWelcomeMessage } from "@client/hooks/useWelcomeMessage";
|
||||
import type { JobStatus } from "@shared/types.js";
|
||||
import type React from "react";
|
||||
|
||||
@@ -8,15 +9,14 @@ interface OrchestratorSummaryProps {
|
||||
}
|
||||
|
||||
export const OrchestratorSummary: React.FC<OrchestratorSummaryProps> = ({
|
||||
stats,
|
||||
isPipelineRunning,
|
||||
}) => {
|
||||
const totalJobs = Object.values(stats).reduce((a, b) => a + b, 0);
|
||||
const welcomeText = useWelcomeMessage();
|
||||
|
||||
return (
|
||||
<section className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<h1 className="text-2xl font-bold tracking-tight">Jobs</h1>
|
||||
<h1 className="text-lg font-medium tracking-tight">{welcomeText}</h1>
|
||||
</div>
|
||||
|
||||
{isPipelineRunning && (
|
||||
@@ -24,39 +24,6 @@ export const OrchestratorSummary: React.FC<OrchestratorSummaryProps> = ({
|
||||
<PipelineProgress isRunning={isPipelineRunning} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Compact metrics summary - demoted visual weight */}
|
||||
<div className="flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-muted-foreground/80">
|
||||
<span>
|
||||
<span className="tabular-nums">{stats.ready}</span> ready
|
||||
</span>
|
||||
<span className="text-border">•</span>
|
||||
<span>
|
||||
<span className="tabular-nums">
|
||||
{stats.discovered + stats.processing}
|
||||
</span>{" "}
|
||||
discovered
|
||||
</span>
|
||||
<span className="text-border">•</span>
|
||||
<span>
|
||||
<span className="tabular-nums">{stats.applied}</span> applied
|
||||
</span>
|
||||
<span className="text-border">•</span>
|
||||
<span className="font-medium text-foreground/60">
|
||||
{totalJobs} jobs total
|
||||
</span>
|
||||
{(stats.skipped > 0 || stats.expired > 0) && (
|
||||
<>
|
||||
<span className="text-border">•</span>
|
||||
<span className="text-muted-foreground/60">
|
||||
<span className="tabular-nums">
|
||||
{stats.skipped + stats.expired}
|
||||
</span>{" "}
|
||||
skipped
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user