mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-23 08:03:33 +00:00
* feat: show the workspace an operator is in, and let them switch Operators get a single burger button instead of the developer sidebar, so nothing on screen named the workspace they were working in. The button now carries the workspace colour as a disc behind the hamburger, with the workspace name beside it — permanently on home, and on hover elsewhere. Workspaces without a colour fall back to a neutral disc so the button always reads as a control against the page behind it. A "Switch workspace" submenu lists the operator's workspaces, reusing the developer sidebar's picker: that list moved out of WorkspaceMenu into WorkspacePickerBody so both render the same rows. "All workspaces" moved from the operator menu into the bottom of that picker, outside its scroll area so it stays reachable. An operator switching workspaces lands on home, since their page access is granted per workspace and the page they are on may not be theirs to open in the one they switch into. Developers keep the existing stay-on-the-page behaviour, as do pickers embedded in a page that drives its own navigation. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmXFP3eaPPdZuVwVpLuYRX * fix: decide the operator landing from positive role signals only `operator_settings` is NULL both for a non-operator and for an operator whose workspace never had settings written, so a `false` from `isOperatorInWorkspace` could not stand as proof of a developer. Lead with `userStore.operator`, an explicit flag for the workspace being left, and keep the target check only as a confirmation. Also states the picker's expansion-seeding invariant as what the host menu actually guarantees: a close and re-open inside its 100ms outro resumes the same instance rather than a fresh one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmXFP3eaPPdZuVwVpLuYRX --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
69 lines
2.9 KiB
TypeScript
69 lines
2.9 KiB
TypeScript
import { base } from '$lib/base'
|
|
import type { UserWorkspace } from '$lib/stores'
|
|
|
|
/**
|
|
* The pages an operator can reach, each paired with the `operator_settings` key
|
|
* that admits them — the operator menu lists only the entries the active
|
|
* workspace permits.
|
|
*/
|
|
export type OperatorMenuLink = {
|
|
label: string
|
|
/** `operator_settings` key gating the page. */
|
|
id: string
|
|
href: string
|
|
}
|
|
|
|
export type OperatorTriggerLink = OperatorMenuLink & { kind: string }
|
|
|
|
/** Home needs no setting — it is the one page every operator always has. */
|
|
export const OPERATOR_MAIN_LINKS: OperatorMenuLink[] = [
|
|
{ label: 'Home', id: 'home', href: `${base}/` },
|
|
{ label: 'Runs', id: 'runs', href: `${base}/runs` },
|
|
{ label: 'Schedules', id: 'schedules', href: `${base}/schedules` }
|
|
]
|
|
|
|
export const OPERATOR_SECONDARY_LINKS: OperatorMenuLink[] = [
|
|
{ label: 'Resources', id: 'resources', href: `${base}/resources` },
|
|
{ label: 'Variables', id: 'variables', href: `${base}/variables` },
|
|
{ label: 'Assets', id: 'assets', href: `${base}/assets` },
|
|
{ label: 'Groups', id: 'groups', href: `${base}/groups` },
|
|
{ label: 'Folders', id: 'folders', href: `${base}/folders` },
|
|
{ label: 'Workers', id: 'workers', href: `${base}/workers` },
|
|
{ label: 'Audit logs', id: 'audit_logs', href: `${base}/audit_logs` }
|
|
]
|
|
|
|
export const OPERATOR_TRIGGER_LINKS: OperatorTriggerLink[] = [
|
|
{ label: 'Custom HTTP routes', id: 'triggers', href: `${base}/routes`, kind: 'http' },
|
|
{ label: 'Websocket triggers', id: 'triggers', href: `${base}/websocket_triggers`, kind: 'ws' },
|
|
{
|
|
label: 'Postgres triggers',
|
|
id: 'triggers',
|
|
href: `${base}/postgres_triggers`,
|
|
kind: 'postgres'
|
|
},
|
|
{ label: 'Kafka triggers', id: 'triggers', href: `${base}/kafka_triggers`, kind: 'kafka' },
|
|
{ label: 'NATS triggers', id: 'triggers', href: `${base}/nats_triggers`, kind: 'nats' },
|
|
{ label: 'SQS triggers', id: 'triggers', href: `${base}/sqs_triggers`, kind: 'sqs' },
|
|
{ label: 'GCP Pub/Sub triggers', id: 'triggers', href: `${base}/gcp_triggers`, kind: 'gcp' },
|
|
{
|
|
label: 'Azure Event Grid triggers',
|
|
id: 'triggers',
|
|
href: `${base}/azure_triggers`,
|
|
kind: 'azure'
|
|
},
|
|
{ label: 'MQTT triggers', id: 'triggers', href: `${base}/mqtt_triggers`, kind: 'mqtt' },
|
|
{ label: 'AMQP triggers', id: 'triggers', href: `${base}/amqp_triggers`, kind: 'amqp' },
|
|
{ label: 'Email triggers', id: 'triggers', href: `${base}/email_triggers`, kind: 'email' }
|
|
]
|
|
|
|
/**
|
|
* Whether the user is an operator in `workspace`. The server nulls
|
|
* `operator_settings` for a non-operator, so a non-null value narrows an operator
|
|
* correctly; the converse does not hold — the column is itself nullable, so a
|
|
* genuine operator whose workspace never had settings written also reads as NULL.
|
|
* Only rely on `true`, never on `false` meaning "not an operator".
|
|
*/
|
|
export function isOperatorInWorkspace(workspace: UserWorkspace | undefined): boolean {
|
|
return workspace?.operator_settings != null
|
|
}
|