Skip to content

Commit 051b223

Browse files
authored
improvement(sim-links): use the shared resource table layout (#9491)
* improvement(sim-links): use the shared resource table layout * improvement(sim-links): match sibling resource pages (loading chrome, shared no-results, menu order, columns) * improvement(sim-links): const sort options; sort menu accepts readonly options * improvement(sim-links): drop the Opens column and give Destination the space * improvement(sim-links): loading chrome until the first page, readonly resource props, docs match the UI * docs(sim-links): tighten the Sim Links page * improvement(sim-links): context-menu anchor uses Tailwind for fixed styles
1 parent 3a39177 commit 051b223

16 files changed

Lines changed: 380 additions & 207 deletions

File tree

‎apps/docs/content/docs/search/sim-links.mdx‎

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,62 +3,56 @@ title: Sim Links
33
description: Short sim/ links that take everyone in your organization to the right page
44
---
55

6-
Sim Links are short, memorable names for the pages your team opens often, like `sim/handbook` for the employee handbook or `sim/okta` for your sign-in portal. Anyone in the organization can open them, with or without a Search seat. They work like go links in other tools.
6+
Sim Links are short names for pages your team opens often, like `sim/handbook` or `sim/okta`. Every member of your organization can use them, with or without a Search seat.
77

8-
Open **Sim Links** in the organization sidebar to see every link in your organization, with who created it, how often it's opened, and when it last changed. Search by name, destination, or description, and select **Most used** to see the 50 links your team opens most.
8+
Open **Sim Links** in the organization sidebar to see your organization's links. Search by name, destination, or description, or use **Sort → Opens** to see the most used links.
99

1010
## Open a link
1111

12-
Go to `https://sim.ai/sim/<name>`, for example `https://sim.ai/sim/handbook`. On a self-hosted deployment, use your Sim address instead of `sim.ai`. Sim sends you to the link's destination. If you aren't signed in, Sim asks you to sign in first, then continues to the link.
12+
Go to `https://www.sim.ai/sim/<name>`, for example `https://www.sim.ai/sim/handbook`. On a self-hosted deployment, use your Sim address. If you aren't signed in, Sim asks you to sign in first.
1313

14-
Names ignore case and punctuation, so `sim/Team-Handbook`, `sim/team_handbook`, and `sim/teamhandbook` all open the same link. The destination still applies its own permissions.
15-
16-
If nobody has created the link yet, Sim offers to create it with that name.
14+
Names ignore case and punctuation, so `sim/Team-Handbook` and `sim/teamhandbook` open the same link. If a link doesn't exist yet, Sim offers to create it.
1715

1816
## Open links from the address bar
1917

20-
Add Sim as a site search shortcut in Chrome so you can type `sim handbook` instead of the full address:
18+
Add Sim as a Chrome site search shortcut to type `sim handbook` instead:
2119

2220
1. Open Chrome **Settings → Search engine → Manage search engines and site search**.
2321
2. Next to **Site search**, select **Add**.
24-
3. Enter `Sim` as the name, `sim` as the shortcut, and `https://sim.ai/sim/%s` as the URL.
25-
26-
Type `sim`, press **Space** or **Tab**, then type a link name such as `handbook` or `jira/123` and press **Enter**.
22+
3. Enter `Sim` as the name, `sim` as the shortcut, and `https://www.sim.ai/sim/%s` as the URL.
2723

28-
Chrome administrators can add this shortcut for everyone with the `SiteSearchSettings` policy, using the same shortcut and URL.
24+
Type `sim`, press **Space**, then type a link name and press **Enter**. Chrome administrators can add the shortcut for everyone with the `SiteSearchSettings` policy.
2925

3026
## Create a link
3127

3228
1. Open **Sim Links** and select **New link**.
33-
2. Enter the **Destination URL**, a full `https://` address.
34-
3. Enter a **Name**. Use letters, numbers, and `. : @ & ~ - _`.
35-
4. Optionally add a **Description** so teammates know what the link is for.
36-
5. Select **Create**.
29+
2. Enter the **Destination URL** and a **Name**. Names can use letters, numbers, and `. : @ & ~ - _`.
30+
3. Optionally add a **Description**, then select **Create**.
3731

38-
Select **Copy link** from a link's menu to share it.
32+
To share a link, right-click it and select **Copy link**.
3933

4034
## Add values to a link
4135

42-
Anything after the name is added to the destination. If `sim/docs` opens `https://docs.example.com`, then `sim/docs/billing/invoices` opens `https://docs.example.com/billing/invoices`.
36+
Anything after the name is added to the destination: if `sim/docs` opens `https://docs.example.com`, then `sim/docs/billing` opens `https://docs.example.com/billing`.
4337

44-
To place a value somewhere else, put `{*}` in the destination URL:
38+
To place a value elsewhere, put `{*}` in the destination URL:
4539

4640
| Destination URL | You open | Sim opens |
4741
| --- | --- | --- |
4842
| `https://jira.example.com/browse/PROD-{*}` | `sim/jira/123` | `https://jira.example.com/browse/PROD-123` |
4943
| `https://wiki.example.com/search?q={*}` | `sim/wiki/expense policy` | `https://wiki.example.com/search?q=expense%20policy` |
5044
| `https://github.com/acme/{*}/issues/{*}` | `sim/issues/api/42` | `https://github.com/acme/api/issues/42` |
5145

52-
Each value fills the next `{*}` in order, and the last `{*}` takes everything that remains. A link with `{*}` can also have a **Default destination** on the same site, which opens when you use the link without a value. `{*}` can't replace the domain, and values can't be `.` or `..`.
46+
Values fill each `{*}` in order, and the last `{*}` takes the rest. A link with `{*}` can set a **Default destination** on the same site for when no value is given. `{*}` can't replace the domain.
5347

5448
## Control access
5549

56-
Under **Access**, the creator of a link can choose:
50+
By default, any member can edit or delete a link. The link's creator and organization admins can change two options under **Access**:
5751

58-
- **Unlist**: hides the link from the Sim Links page for everyone except the creator and organization admins. Anyone who knows the name can still open it.
59-
- **Restrict editing**: only the creator and organization admins can edit or delete the link. Everyone can still open it.
52+
- **Unlisted**: only the creator and admins see the link on the Sim Links page. Anyone with the name can still open it.
53+
- **Restricted**: only the creator and admins can edit or delete the link.
6054

61-
Without restrictions, any member can edit or delete a link. Only the creator and organization admins can change its access settings. Creating, editing, and deleting links is recorded in your organization's [audit logs](/platform/enterprise/audit-logs).
55+
Changes to links are recorded in your organization's [audit logs](/platform/enterprise/audit-logs).
6256

6357
## Related docs
6458

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
11
export { PAGE_COLUMN_CLASS } from '@/app/o/[organizationId]/components/organization-page/constants'
2-
export {
3-
OrganizationPage,
4-
type OrganizationPageTab,
5-
} from '@/app/o/[organizationId]/components/organization-page/organization-page'
2+
export { OrganizationPage } from '@/app/o/[organizationId]/components/organization-page/organization-page'
63
export { useOrganizationPageFilters } from '@/app/o/[organizationId]/components/organization-page/use-organization-page-filters'

‎apps/sim/app/o/[organizationId]/components/organization-page/organization-page.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {
2020
SIDEBAR_DIVIDER_PAD_BELOW_CLASS,
2121
} from '@/app/workspace/[workspaceId]/w/components/sidebar/constants'
2222

23-
export interface OrganizationPageTab {
23+
interface OrganizationPageTab {
2424
id: string
2525
label: string
2626
}
Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,34 @@
1-
import { cn } from '@sim/emcn'
2-
import { HEADER_ACTION_CLUSTER, PAGE_HEADER_BAR } from '@/components/page-header-bar'
3-
import { PAGE_COLUMN_CLASS } from '@/app/o/[organizationId]/components/organization-page'
1+
'use client'
2+
3+
import { Link, Plus } from '@sim/emcn/icons'
4+
import {
5+
type ChromeActionSpec,
6+
ResourceChromeFallback,
7+
} from '@/app/workspace/[workspaceId]/components/resource/components/resource-chrome-fallback'
8+
import type { ResourceColumn } from '@/app/workspace/[workspaceId]/components/resource/resource'
9+
10+
const COLUMNS = [
11+
{ id: 'name', header: 'Name' },
12+
{ id: 'destination', header: 'Destination', widthMultiplier: 2 },
13+
{ id: 'description', header: 'Description', widthMultiplier: 1.4 },
14+
{ id: 'owner', header: 'Created by' },
15+
{ id: 'access', header: 'Access' },
16+
{ id: 'updated', header: 'Updated' },
17+
] as const satisfies readonly ResourceColumn[]
18+
19+
const ACTIONS = [
20+
{ text: 'New link', icon: Plus, variant: 'primary' },
21+
] as const satisfies readonly ChromeActionSpec[]
422

523
export default function SimLinksLoading() {
624
return (
7-
<div className='flex h-full flex-col bg-[var(--bg)]'>
8-
<div className={PAGE_HEADER_BAR}>
9-
<div className={HEADER_ACTION_CLUSTER} />
10-
</div>
11-
<div className={cn(PAGE_COLUMN_CLASS, 'pt-8')}>
12-
<h1 className='text-[var(--text-primary)] text-lg'>Sim Links</h1>
13-
<p role='status' className='mt-4 text-[var(--text-muted)] text-small'>
14-
Loading Sim links
15-
</p>
16-
</div>
17-
</div>
25+
<ResourceChromeFallback
26+
icon={Link}
27+
title='Sim Links'
28+
columns={COLUMNS}
29+
actions={ACTIONS}
30+
searchPlaceholder='Search Sim links...'
31+
hasSort
32+
/>
1833
)
1934
}
Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,22 @@
11
import { createSerializer, parseAsString } from 'nuqs/server'
2+
import { SIM_LINK_SORTS } from '@/lib/sim-links/validation'
3+
import { createSortParams } from '@/lib/url-state'
24

35
/** `create` opens the new-link editor, prefilled with its value as the name. */
4-
export const simLinkViewParsers = { create: parseAsString } as const
6+
const simLinkViewParsers = { create: parseAsString } as const
57

68
export const serializeSimLinkView = createSerializer(simLinkViewParsers)
9+
10+
/** `search` filters by name, destination, or description; only its URL write is debounced. */
11+
export const simLinksParsers = {
12+
...simLinkViewParsers,
13+
search: parseAsString.withDefault(''),
14+
} as const
15+
16+
/** Shared `sort` + `dir` params for the Sim Links list. Default: alphabetical. */
17+
export const simLinksSortParams = createSortParams(SIM_LINK_SORTS, {
18+
column: 'name',
19+
direction: 'asc',
20+
})
21+
22+
export const simLinksUrlKeys = { history: 'replace', clearOnDefault: true } as const
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
'use client'
2+
3+
import {
4+
DropdownMenu,
5+
DropdownMenuContent,
6+
DropdownMenuItem,
7+
DropdownMenuSeparator,
8+
DropdownMenuTrigger,
9+
} from '@sim/emcn'
10+
import { ArrowUpRight, Duplicate, Pencil, Trash } from '@sim/emcn/icons'
11+
12+
interface SimLinkContextMenuProps {
13+
isOpen: boolean
14+
position: { x: number; y: number }
15+
onClose: () => void
16+
onOpen: () => void
17+
onCopyLink: () => void
18+
/** Present only when the viewer may edit the link. */
19+
onEdit?: () => void
20+
/** Present only when the viewer may delete the link. */
21+
onDelete?: () => void
22+
}
23+
24+
/** Row actions for one Sim link, anchored where the row was right-clicked. */
25+
export function SimLinkContextMenu({
26+
isOpen,
27+
position,
28+
onClose,
29+
onOpen,
30+
onCopyLink,
31+
onEdit,
32+
onDelete,
33+
}: SimLinkContextMenuProps) {
34+
return (
35+
<DropdownMenu open={isOpen} onOpenChange={(open) => !open && onClose()} modal={false}>
36+
<DropdownMenuTrigger asChild>
37+
<div
38+
className='pointer-events-none fixed size-px'
39+
style={{ left: `${position.x}px`, top: `${position.y}px` }}
40+
tabIndex={-1}
41+
aria-hidden
42+
/>
43+
</DropdownMenuTrigger>
44+
<DropdownMenuContent
45+
align='start'
46+
side='bottom'
47+
sideOffset={4}
48+
onCloseAutoFocus={(e) => e.preventDefault()}
49+
>
50+
<DropdownMenuItem onSelect={onOpen}>
51+
<ArrowUpRight />
52+
Open
53+
</DropdownMenuItem>
54+
<DropdownMenuItem onSelect={onCopyLink}>
55+
<Duplicate />
56+
Copy link
57+
</DropdownMenuItem>
58+
{onEdit && (
59+
<DropdownMenuItem onSelect={onEdit}>
60+
<Pencil />
61+
Edit
62+
</DropdownMenuItem>
63+
)}
64+
{onDelete && <DropdownMenuSeparator />}
65+
{onDelete && (
66+
<DropdownMenuItem onSelect={onDelete}>
67+
<Trash />
68+
Delete
69+
</DropdownMenuItem>
70+
)}
71+
</DropdownMenuContent>
72+
</DropdownMenu>
73+
)
74+
}

0 commit comments

Comments
 (0)