@kelpie/ui 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/resources/attendances.d.ts +13 -0
- package/dist/api/resources/attendances.d.ts.map +1 -0
- package/dist/api/resources/attendances.js +34 -0
- package/dist/api/resources/attendances.js.map +1 -0
- package/dist/api/resources/eventAssociations.d.ts +12 -0
- package/dist/api/resources/eventAssociations.d.ts.map +1 -0
- package/dist/api/resources/eventAssociations.js +25 -0
- package/dist/api/resources/eventAssociations.js.map +1 -0
- package/dist/api/resources/events.d.ts +17 -0
- package/dist/api/resources/events.d.ts.map +1 -0
- package/dist/api/resources/events.js +40 -0
- package/dist/api/resources/events.js.map +1 -0
- package/dist/api/resources/handbookSeed.d.ts +11 -0
- package/dist/api/resources/handbookSeed.d.ts.map +1 -0
- package/dist/api/resources/handbookSeed.js +43 -0
- package/dist/api/resources/handbookSeed.js.map +1 -0
- package/dist/api/resources/moduleSettings.d.ts +10 -0
- package/dist/api/resources/moduleSettings.d.ts.map +1 -1
- package/dist/api/resources/moduleSettings.js +21 -0
- package/dist/api/resources/moduleSettings.js.map +1 -1
- package/dist/api/resources/planItems.d.ts +4 -4
- package/dist/api/resources/planItems.d.ts.map +1 -1
- package/dist/api/resources/planItems.js +1 -1
- package/dist/api/resources/planItems.js.map +1 -1
- package/dist/api/resources/publicConfig.js +3 -3
- package/dist/api/resources/sampleData.d.ts.map +1 -1
- package/dist/api/resources/sampleData.js +2 -0
- package/dist/api/resources/sampleData.js.map +1 -1
- package/dist/api/resources/session.d.ts +22 -1
- package/dist/api/resources/session.d.ts.map +1 -1
- package/dist/api/resources/session.js +43 -1
- package/dist/api/resources/session.js.map +1 -1
- package/dist/api/resources/workspace.d.ts.map +1 -1
- package/dist/api/resources/workspace.js +2 -3
- package/dist/api/resources/workspace.js.map +1 -1
- package/dist/app/KelpieApp.d.ts.map +1 -1
- package/dist/app/KelpieApp.js +5 -1
- package/dist/app/KelpieApp.js.map +1 -1
- package/dist/components/ActivitiesPanel.d.ts.map +1 -1
- package/dist/components/ActivitiesPanel.js +2 -0
- package/dist/components/ActivitiesPanel.js.map +1 -1
- package/dist/components/AddressesField.d.ts +23 -0
- package/dist/components/AddressesField.d.ts.map +1 -0
- package/dist/components/AddressesField.js +215 -0
- package/dist/components/AddressesField.js.map +1 -0
- package/dist/components/CountrySearch.d.ts +16 -0
- package/dist/components/CountrySearch.d.ts.map +1 -0
- package/dist/components/CountrySearch.js +30 -0
- package/dist/components/CountrySearch.js.map +1 -0
- package/dist/components/EnvironmentBanner.d.ts +0 -12
- package/dist/components/EnvironmentBanner.d.ts.map +1 -1
- package/dist/components/EnvironmentBanner.js +21 -6
- package/dist/components/EnvironmentBanner.js.map +1 -1
- package/dist/components/InlineEdit.d.ts +7 -1
- package/dist/components/InlineEdit.d.ts.map +1 -1
- package/dist/components/InlineEdit.js +8 -1
- package/dist/components/InlineEdit.js.map +1 -1
- package/dist/components/LinkedEventsPanel.d.ts +11 -0
- package/dist/components/LinkedEventsPanel.d.ts.map +1 -0
- package/dist/components/LinkedEventsPanel.js +18 -0
- package/dist/components/LinkedEventsPanel.js.map +1 -0
- package/dist/components/NotesPanel.d.ts +4 -6
- package/dist/components/NotesPanel.d.ts.map +1 -1
- package/dist/components/NotesPanel.js +39 -3
- package/dist/components/NotesPanel.js.map +1 -1
- package/dist/components/PlanAttention.d.ts.map +1 -1
- package/dist/components/PlanAttention.js +3 -2
- package/dist/components/PlanAttention.js.map +1 -1
- package/dist/components/PlanPanel.d.ts +3 -3
- package/dist/components/PlanPanel.d.ts.map +1 -1
- package/dist/components/Shell.d.ts.map +1 -1
- package/dist/components/Shell.js +8 -2
- package/dist/components/Shell.js.map +1 -1
- package/dist/components/SocialNetworkIcon.d.ts +17 -0
- package/dist/components/SocialNetworkIcon.d.ts.map +1 -0
- package/dist/components/SocialNetworkIcon.js +39 -0
- package/dist/components/SocialNetworkIcon.js.map +1 -0
- package/dist/components/SocialProfilesField.d.ts +8 -0
- package/dist/components/SocialProfilesField.d.ts.map +1 -1
- package/dist/components/SocialProfilesField.js +19 -5
- package/dist/components/SocialProfilesField.js.map +1 -1
- package/dist/components/TimezoneSearch.d.ts +21 -0
- package/dist/components/TimezoneSearch.d.ts.map +1 -0
- package/dist/components/TimezoneSearch.js +27 -0
- package/dist/components/TimezoneSearch.js.map +1 -0
- package/dist/components/WorkspaceSwitcher.d.ts +8 -0
- package/dist/components/WorkspaceSwitcher.d.ts.map +1 -0
- package/dist/components/WorkspaceSwitcher.js +67 -0
- package/dist/components/WorkspaceSwitcher.js.map +1 -0
- package/dist/components/environmentTone.d.ts +10 -0
- package/dist/components/environmentTone.d.ts.map +1 -0
- package/dist/components/environmentTone.js +35 -0
- package/dist/components/environmentTone.js.map +1 -0
- package/dist/index.d.ts +21 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -2
- package/dist/index.js.map +1 -1
- package/dist/lib/timezones.d.ts +17 -0
- package/dist/lib/timezones.d.ts.map +1 -0
- package/dist/lib/timezones.js +39 -0
- package/dist/lib/timezones.js.map +1 -0
- package/dist/pages/CompaniesPage.d.ts.map +1 -1
- package/dist/pages/CompaniesPage.js +12 -11
- package/dist/pages/CompaniesPage.js.map +1 -1
- package/dist/pages/CompanyDetail.d.ts.map +1 -1
- package/dist/pages/CompanyDetail.js +23 -12
- package/dist/pages/CompanyDetail.js.map +1 -1
- package/dist/pages/DealDetail.d.ts.map +1 -1
- package/dist/pages/DealDetail.js +5 -1
- package/dist/pages/DealDetail.js.map +1 -1
- package/dist/pages/DecisionsPage.d.ts.map +1 -1
- package/dist/pages/DecisionsPage.js +7 -0
- package/dist/pages/DecisionsPage.js.map +1 -1
- package/dist/pages/EnquiryDetail.d.ts.map +1 -1
- package/dist/pages/EnquiryDetail.js +5 -1
- package/dist/pages/EnquiryDetail.js.map +1 -1
- package/dist/pages/EventDetail.d.ts +2 -0
- package/dist/pages/EventDetail.d.ts.map +1 -0
- package/dist/pages/EventDetail.js +332 -0
- package/dist/pages/EventDetail.js.map +1 -0
- package/dist/pages/EventsPage.d.ts +2 -0
- package/dist/pages/EventsPage.d.ts.map +1 -0
- package/dist/pages/EventsPage.js +176 -0
- package/dist/pages/EventsPage.js.map +1 -0
- package/dist/pages/ListDetail.d.ts.map +1 -1
- package/dist/pages/ListDetail.js +17 -0
- package/dist/pages/ListDetail.js.map +1 -1
- package/dist/pages/OpportunityDetail.d.ts.map +1 -1
- package/dist/pages/OpportunityDetail.js +5 -1
- package/dist/pages/OpportunityDetail.js.map +1 -1
- package/dist/pages/PartnershipDetail.d.ts.map +1 -1
- package/dist/pages/PartnershipDetail.js +5 -1
- package/dist/pages/PartnershipDetail.js.map +1 -1
- package/dist/pages/PeoplePage.d.ts.map +1 -1
- package/dist/pages/PeoplePage.js +13 -5
- package/dist/pages/PeoplePage.js.map +1 -1
- package/dist/pages/PersonDetail.d.ts.map +1 -1
- package/dist/pages/PersonDetail.js +94 -28
- package/dist/pages/PersonDetail.js.map +1 -1
- package/dist/pages/PlanningPage.d.ts.map +1 -1
- package/dist/pages/PlanningPage.js +6 -3
- package/dist/pages/PlanningPage.js.map +1 -1
- package/dist/pages/RaiseDetail.d.ts.map +1 -1
- package/dist/pages/RaiseDetail.js +5 -1
- package/dist/pages/RaiseDetail.js.map +1 -1
- package/dist/pages/RoleDetail.d.ts.map +1 -1
- package/dist/pages/RoleDetail.js +4 -1
- package/dist/pages/RoleDetail.js.map +1 -1
- package/dist/pages/SearchPage.d.ts.map +1 -1
- package/dist/pages/SearchPage.js +2 -1
- package/dist/pages/SearchPage.js.map +1 -1
- package/dist/pages/account/PreferencesPage.d.ts.map +1 -1
- package/dist/pages/account/PreferencesPage.js +2 -28
- package/dist/pages/account/PreferencesPage.js.map +1 -1
- package/dist/pages/admin/DataPage.js +10 -9
- package/dist/pages/admin/DataPage.js.map +1 -1
- package/dist/pages/admin/FieldsPage.js +1 -1
- package/dist/pages/admin/FieldsPage.js.map +1 -1
- package/dist/pages/admin/ModulesPage.d.ts.map +1 -1
- package/dist/pages/admin/ModulesPage.js +1 -0
- package/dist/pages/admin/ModulesPage.js.map +1 -1
- package/dist/pages/admin/WorkspaceSettingsPage.d.ts +12 -0
- package/dist/pages/admin/WorkspaceSettingsPage.d.ts.map +1 -1
- package/dist/pages/admin/WorkspaceSettingsPage.js +2 -21
- package/dist/pages/admin/WorkspaceSettingsPage.js.map +1 -1
- package/dist/pages/auth/AuthLayout.d.ts +5 -4
- package/dist/pages/auth/AuthLayout.d.ts.map +1 -1
- package/dist/pages/auth/AuthLayout.js +7 -5
- package/dist/pages/auth/AuthLayout.js.map +1 -1
- package/dist/pages/auth/RegionSwitcher.d.ts +10 -0
- package/dist/pages/auth/RegionSwitcher.d.ts.map +1 -0
- package/dist/pages/auth/RegionSwitcher.js +28 -0
- package/dist/pages/auth/RegionSwitcher.js.map +1 -0
- package/dist/pages/auth/SignInPage.d.ts.map +1 -1
- package/dist/pages/auth/SignInPage.js +3 -1
- package/dist/pages/auth/SignInPage.js.map +1 -1
- package/dist/pages/auth/SignUpPage.d.ts.map +1 -1
- package/dist/pages/auth/SignUpPage.js +7 -0
- package/dist/pages/auth/SignUpPage.js.map +1 -1
- package/dist/pages/auth/regionHref.d.ts +6 -0
- package/dist/pages/auth/regionHref.d.ts.map +1 -0
- package/dist/pages/auth/regionHref.js +8 -0
- package/dist/pages/auth/regionHref.js.map +1 -0
- package/dist/pages/dashboard/DashboardPage.d.ts +8 -0
- package/dist/pages/dashboard/DashboardPage.d.ts.map +1 -1
- package/dist/pages/dashboard/DashboardPage.js +5 -13
- package/dist/pages/dashboard/DashboardPage.js.map +1 -1
- package/dist/pages/dashboard/attention.d.ts +1 -8
- package/dist/pages/dashboard/attention.d.ts.map +1 -1
- package/dist/pages/dashboard/attention.js +21 -54
- package/dist/pages/dashboard/attention.js.map +1 -1
- package/dist/pages/dashboard/sections.d.ts.map +1 -1
- package/dist/pages/dashboard/sections.js +3 -2
- package/dist/pages/dashboard/sections.js.map +1 -1
- package/dist/pages/forms/FormActions.d.ts.map +1 -1
- package/dist/pages/forms/FormActions.js +7 -3
- package/dist/pages/forms/FormActions.js.map +1 -1
- package/dist/pages/onboarding/HandbookStepPage.d.ts +4 -8
- package/dist/pages/onboarding/HandbookStepPage.d.ts.map +1 -1
- package/dist/pages/onboarding/HandbookStepPage.js +13 -13
- package/dist/pages/onboarding/HandbookStepPage.js.map +1 -1
- package/dist/pages/onboarding/InvitesStepPage.d.ts.map +1 -1
- package/dist/pages/onboarding/InvitesStepPage.js +13 -7
- package/dist/pages/onboarding/InvitesStepPage.js.map +1 -1
- package/dist/pages/onboarding/ModulesStepPage.d.ts +11 -0
- package/dist/pages/onboarding/ModulesStepPage.d.ts.map +1 -0
- package/dist/pages/onboarding/ModulesStepPage.js +80 -0
- package/dist/pages/onboarding/ModulesStepPage.js.map +1 -0
- package/dist/pages/onboarding/OnboardingNav.d.ts +17 -0
- package/dist/pages/onboarding/OnboardingNav.d.ts.map +1 -0
- package/dist/pages/onboarding/OnboardingNav.js +37 -0
- package/dist/pages/onboarding/OnboardingNav.js.map +1 -0
- package/dist/pages/onboarding/OrganisationStepPage.d.ts +13 -0
- package/dist/pages/onboarding/OrganisationStepPage.d.ts.map +1 -0
- package/dist/pages/onboarding/OrganisationStepPage.js +59 -0
- package/dist/pages/onboarding/OrganisationStepPage.js.map +1 -0
- package/dist/pages/onboarding/WorkspaceStepPage.d.ts.map +1 -1
- package/dist/pages/onboarding/WorkspaceStepPage.js +25 -20
- package/dist/pages/onboarding/WorkspaceStepPage.js.map +1 -1
- package/dist/pages/onboarding/handbookTemplates.d.ts +19 -0
- package/dist/pages/onboarding/handbookTemplates.d.ts.map +1 -0
- package/dist/pages/onboarding/handbookTemplates.js +57 -0
- package/dist/pages/onboarding/handbookTemplates.js.map +1 -0
- package/dist/pages/onboarding/onboardingModules.d.ts +23 -0
- package/dist/pages/onboarding/onboardingModules.d.ts.map +1 -0
- package/dist/pages/onboarding/onboardingModules.js +153 -0
- package/dist/pages/onboarding/onboardingModules.js.map +1 -0
- package/dist/pages/onboarding/onboardingRerun.d.ts +21 -0
- package/dist/pages/onboarding/onboardingRerun.d.ts.map +1 -0
- package/dist/pages/onboarding/onboardingRerun.js +33 -0
- package/dist/pages/onboarding/onboardingRerun.js.map +1 -0
- package/package.json +4 -3
- package/src/api/resources/attendances.ts +54 -0
- package/src/api/resources/eventAssociations.ts +43 -0
- package/src/api/resources/events.ts +70 -0
- package/src/api/resources/handbookSeed.ts +63 -0
- package/src/api/resources/moduleSettings.ts +25 -0
- package/src/api/resources/planItems.ts +5 -5
- package/src/api/resources/publicConfig.ts +3 -3
- package/src/api/resources/sampleData.ts +2 -0
- package/src/api/resources/session.ts +59 -0
- package/src/api/resources/workspace.ts +2 -3
- package/src/app/KelpieApp.tsx +11 -3
- package/src/components/ActivitiesPanel.tsx +2 -0
- package/src/components/AddressesField.tsx +502 -0
- package/src/components/CountrySearch.tsx +71 -0
- package/src/components/EnvironmentBanner.tsx +23 -6
- package/src/components/InlineEdit.tsx +35 -1
- package/src/components/LinkedEventsPanel.tsx +50 -0
- package/src/components/NotesPanel.tsx +132 -15
- package/src/components/PlanAttention.tsx +5 -4
- package/src/components/PlanPanel.tsx +3 -3
- package/src/components/Shell.tsx +16 -1
- package/src/components/SocialNetworkIcon.tsx +88 -0
- package/src/components/SocialProfilesField.tsx +57 -10
- package/src/components/TimezoneSearch.tsx +67 -0
- package/src/components/WorkspaceSwitcher.tsx +136 -0
- package/src/components/environmentTone.ts +40 -0
- package/src/index.ts +42 -3
- package/src/lib/timezones.ts +43 -0
- package/src/pages/CompaniesPage.tsx +14 -13
- package/src/pages/CompanyDetail.tsx +59 -27
- package/src/pages/DealDetail.tsx +5 -0
- package/src/pages/DecisionsPage.tsx +7 -0
- package/src/pages/EnquiryDetail.tsx +7 -0
- package/src/pages/EventDetail.tsx +794 -0
- package/src/pages/EventsPage.tsx +382 -0
- package/src/pages/ListDetail.tsx +24 -0
- package/src/pages/OpportunityDetail.tsx +7 -0
- package/src/pages/PartnershipDetail.tsx +7 -0
- package/src/pages/PeoplePage.tsx +18 -5
- package/src/pages/PersonDetail.tsx +300 -100
- package/src/pages/PlanningPage.tsx +17 -14
- package/src/pages/RaiseDetail.tsx +5 -0
- package/src/pages/RoleDetail.tsx +8 -0
- package/src/pages/SearchPage.tsx +2 -1
- package/src/pages/account/PreferencesPage.tsx +2 -37
- package/src/pages/admin/DataPage.tsx +63 -21
- package/src/pages/admin/FieldsPage.tsx +1 -1
- package/src/pages/admin/ModulesPage.tsx +1 -0
- package/src/pages/admin/WorkspaceSettingsPage.tsx +10 -33
- package/src/pages/auth/AuthLayout.tsx +8 -4
- package/src/pages/auth/RegionSwitcher.tsx +56 -0
- package/src/pages/auth/SignInPage.tsx +10 -6
- package/src/pages/auth/SignUpPage.tsx +25 -0
- package/src/pages/auth/regionHref.ts +10 -0
- package/src/pages/dashboard/DashboardPage.tsx +16 -48
- package/src/pages/dashboard/attention.ts +24 -78
- package/src/pages/dashboard/sections.tsx +8 -2
- package/src/pages/forms/FormActions.tsx +14 -8
- package/src/pages/onboarding/HandbookStepPage.tsx +21 -24
- package/src/pages/onboarding/InvitesStepPage.tsx +27 -19
- package/src/pages/onboarding/ModulesStepPage.tsx +145 -0
- package/src/pages/onboarding/OnboardingNav.tsx +97 -0
- package/src/pages/onboarding/OrganisationStepPage.tsx +130 -0
- package/src/pages/onboarding/WorkspaceStepPage.tsx +53 -38
- package/src/pages/onboarding/handbookTemplates.ts +77 -0
- package/src/pages/onboarding/onboardingModules.ts +202 -0
- package/src/pages/onboarding/onboardingRerun.ts +41 -0
- package/src/styles.css +30 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { EventAssociationTargetType } from '@kelpie/schemas'
|
|
2
|
+
import { Link } from 'react-router'
|
|
3
|
+
|
|
4
|
+
import { useEventAssociationsFor } from '../api/resources/eventAssociations.ts'
|
|
5
|
+
import { ErrorPanel } from './QueryState.tsx'
|
|
6
|
+
import { SectionHeader } from './SectionHeader.tsx'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Events associated with another record (a Deal, Company, Role, …).
|
|
10
|
+
*
|
|
11
|
+
* Registrants are Attendances on the Person, not this list.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export function LinkedEventsPanel({
|
|
15
|
+
targetType,
|
|
16
|
+
targetId,
|
|
17
|
+
}: {
|
|
18
|
+
readonly targetType: EventAssociationTargetType
|
|
19
|
+
readonly targetId: string
|
|
20
|
+
}): React.JSX.Element {
|
|
21
|
+
const { associations, isLoading, error } = useEventAssociationsFor(targetType, targetId)
|
|
22
|
+
|
|
23
|
+
if (error !== null) {
|
|
24
|
+
return <ErrorPanel error={error} />
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<div>
|
|
29
|
+
<SectionHeader title="Events" />
|
|
30
|
+
{isLoading && <p className="text-[13px] text-ink-faint">Loading events…</p>}
|
|
31
|
+
{!isLoading && associations.length === 0 && (
|
|
32
|
+
<p className="text-[13px] text-ink-faint">No events linked yet.</p>
|
|
33
|
+
)}
|
|
34
|
+
{associations.length > 0 && (
|
|
35
|
+
<ul className="divide-y divide-border">
|
|
36
|
+
{associations.map((row) => (
|
|
37
|
+
<li key={row.eventId} className="py-2">
|
|
38
|
+
<Link
|
|
39
|
+
to={`/events/${row.eventId}`}
|
|
40
|
+
className="text-[13px] font-medium text-accent hover:underline"
|
|
41
|
+
>
|
|
42
|
+
{row.eventName}
|
|
43
|
+
</Link>
|
|
44
|
+
</li>
|
|
45
|
+
))}
|
|
46
|
+
</ul>
|
|
47
|
+
)}
|
|
48
|
+
</div>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
@@ -4,8 +4,9 @@ import type { FormEvent } from 'react'
|
|
|
4
4
|
|
|
5
5
|
import { useTimezone } from '../api/resources/account.ts'
|
|
6
6
|
import { useMembers } from '../api/resources/members.ts'
|
|
7
|
-
import { useCreateNote, useNotes } from '../api/resources/notes.ts'
|
|
7
|
+
import { useCreateNote, useDeleteNote, useNotes, useUpdateNote } from '../api/resources/notes.ts'
|
|
8
8
|
import { formatDateTime } from '../lib/dates.ts'
|
|
9
|
+
import { MarkdownView } from './MarkdownView.tsx'
|
|
9
10
|
import { Paginator } from './Paginator.tsx'
|
|
10
11
|
import { ErrorPanel } from './QueryState.tsx'
|
|
11
12
|
import { SectionHeader } from './SectionHeader.tsx'
|
|
@@ -13,12 +14,10 @@ import { SectionHeader } from './SectionHeader.tsx'
|
|
|
13
14
|
/**
|
|
14
15
|
* The notes on one record.
|
|
15
16
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* mockup
|
|
19
|
-
* agent can still pin.
|
|
20
|
-
*
|
|
21
|
-
* No edit or delete either, for the same reason, though the API has both.
|
|
17
|
+
* Bodies are markdown, rendered with the same component as the handbook.
|
|
18
|
+
* Notes can be created, edited, and deleted here. There is no pin control: the
|
|
19
|
+
* mockup renders the badge on a note that carries the flag and offers no way to
|
|
20
|
+
* set it. `PATCH /v1/notes/:id` takes `pinned`, so an agent can still pin.
|
|
22
21
|
*/
|
|
23
22
|
|
|
24
23
|
export interface NotesPanelProps {
|
|
@@ -82,7 +81,7 @@ export function NotesPanel({ targetType, targetId }: NotesPanelProps): React.JSX
|
|
|
82
81
|
onChange={(event) => {
|
|
83
82
|
setBody(event.target.value)
|
|
84
83
|
}}
|
|
85
|
-
placeholder="Write a note…"
|
|
84
|
+
placeholder="Write a note… (Markdown supported)"
|
|
86
85
|
rows={3}
|
|
87
86
|
autoFocus
|
|
88
87
|
className="w-full resize-y rounded-md border border-border bg-surface-raised px-3 py-2 text-[13px] outline-none focus:border-accent focus:ring-2 focus:ring-accent/20"
|
|
@@ -143,22 +142,140 @@ function NoteItem({
|
|
|
143
142
|
readonly authorName: string
|
|
144
143
|
}): React.JSX.Element {
|
|
145
144
|
const timezone = useTimezone()
|
|
145
|
+
const updateNote = useUpdateNote()
|
|
146
|
+
const deleteNote = useDeleteNote()
|
|
147
|
+
const [editing, setEditing] = useState(false)
|
|
148
|
+
const [confirmingDelete, setConfirmingDelete] = useState(false)
|
|
149
|
+
const [draft, setDraft] = useState(note.body)
|
|
150
|
+
|
|
151
|
+
function cancelEdit(): void {
|
|
152
|
+
setDraft(note.body)
|
|
153
|
+
setEditing(false)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function submitEdit(event: FormEvent): void {
|
|
157
|
+
event.preventDefault()
|
|
158
|
+
|
|
159
|
+
const text = draft.trim()
|
|
160
|
+
|
|
161
|
+
if (text.length === 0) {
|
|
162
|
+
return
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (text !== note.body) {
|
|
166
|
+
updateNote.run({ id: note.id, changes: { body: text } })
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
setEditing(false)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (editing) {
|
|
173
|
+
return (
|
|
174
|
+
<li className="rounded-md border border-border bg-surface-raised px-3.5 py-3">
|
|
175
|
+
<form onSubmit={submitEdit} className="space-y-2">
|
|
176
|
+
<textarea
|
|
177
|
+
value={draft}
|
|
178
|
+
onChange={(event) => {
|
|
179
|
+
setDraft(event.target.value)
|
|
180
|
+
}}
|
|
181
|
+
rows={3}
|
|
182
|
+
autoFocus
|
|
183
|
+
className="w-full resize-y rounded-md border border-border bg-surface-raised px-3 py-2 text-[13px] outline-none focus:border-accent focus:ring-2 focus:ring-accent/20"
|
|
184
|
+
/>
|
|
185
|
+
<div className="flex justify-end gap-2">
|
|
186
|
+
<button
|
|
187
|
+
type="button"
|
|
188
|
+
onClick={cancelEdit}
|
|
189
|
+
className="rounded-md px-2.5 py-1 text-[12px] font-medium text-ink-muted hover:text-ink"
|
|
190
|
+
>
|
|
191
|
+
Cancel
|
|
192
|
+
</button>
|
|
193
|
+
<button
|
|
194
|
+
type="submit"
|
|
195
|
+
className="rounded-md bg-accent px-3 py-1.5 text-[12px] font-semibold text-accent-fg hover:bg-accent-hover"
|
|
196
|
+
>
|
|
197
|
+
Save
|
|
198
|
+
</button>
|
|
199
|
+
</div>
|
|
200
|
+
</form>
|
|
201
|
+
{updateNote.error !== null && (
|
|
202
|
+
<div className="mt-2">
|
|
203
|
+
<ErrorPanel error={updateNote.error} />
|
|
204
|
+
</div>
|
|
205
|
+
)}
|
|
206
|
+
</li>
|
|
207
|
+
)
|
|
208
|
+
}
|
|
146
209
|
|
|
147
210
|
return (
|
|
148
|
-
<li className="rounded-md border border-border bg-surface-raised px-3.5 py-3">
|
|
211
|
+
<li className="group rounded-md border border-border bg-surface-raised px-3.5 py-3">
|
|
149
212
|
<div className="flex items-start justify-between gap-2">
|
|
150
|
-
<
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
213
|
+
<div className="min-w-0 flex-1">
|
|
214
|
+
<MarkdownView source={note.body} />
|
|
215
|
+
</div>
|
|
216
|
+
<div className="flex shrink-0 flex-col items-end gap-1">
|
|
217
|
+
{note.pinned && (
|
|
218
|
+
<span className="text-[10px] font-semibold tracking-wide text-accent uppercase">Pinned</span>
|
|
219
|
+
)}
|
|
220
|
+
{confirmingDelete ? (
|
|
221
|
+
<div className="flex flex-wrap items-center justify-end gap-2">
|
|
222
|
+
<span className="text-[11px] text-ink-muted">Delete this note?</span>
|
|
223
|
+
<button
|
|
224
|
+
type="button"
|
|
225
|
+
onClick={() => {
|
|
226
|
+
setConfirmingDelete(false)
|
|
227
|
+
}}
|
|
228
|
+
className="rounded-md px-2 py-0.5 text-[11px] font-medium text-ink-muted hover:text-ink"
|
|
229
|
+
>
|
|
230
|
+
Cancel
|
|
231
|
+
</button>
|
|
232
|
+
<button
|
|
233
|
+
type="button"
|
|
234
|
+
disabled={deleteNote.isPending}
|
|
235
|
+
onClick={() => {
|
|
236
|
+
deleteNote.run(note.id)
|
|
237
|
+
setConfirmingDelete(false)
|
|
238
|
+
}}
|
|
239
|
+
className="rounded-md bg-danger px-2 py-0.5 text-[11px] font-semibold text-danger-fg transition hover:opacity-90 disabled:opacity-50"
|
|
240
|
+
>
|
|
241
|
+
{deleteNote.isPending ? 'Deleting…' : 'Delete note'}
|
|
242
|
+
</button>
|
|
243
|
+
</div>
|
|
244
|
+
) : (
|
|
245
|
+
<span className="flex gap-1 opacity-0 transition-opacity group-hover:opacity-100 focus-within:opacity-100">
|
|
246
|
+
<button
|
|
247
|
+
type="button"
|
|
248
|
+
onClick={() => {
|
|
249
|
+
setDraft(note.body)
|
|
250
|
+
setEditing(true)
|
|
251
|
+
}}
|
|
252
|
+
className="rounded-md px-2 py-0.5 text-[11px] font-medium text-ink-muted hover:bg-surface-sunken hover:text-ink"
|
|
253
|
+
>
|
|
254
|
+
Edit
|
|
255
|
+
</button>
|
|
256
|
+
<button
|
|
257
|
+
type="button"
|
|
258
|
+
onClick={() => {
|
|
259
|
+
setConfirmingDelete(true)
|
|
260
|
+
}}
|
|
261
|
+
className="rounded-md px-2 py-0.5 text-[11px] font-medium text-ink-muted hover:bg-danger-soft hover:text-danger"
|
|
262
|
+
>
|
|
263
|
+
Delete
|
|
264
|
+
</button>
|
|
265
|
+
</span>
|
|
266
|
+
)}
|
|
267
|
+
</div>
|
|
156
268
|
</div>
|
|
157
269
|
<div className="mt-2 flex items-center gap-2 text-[11px] text-ink-faint">
|
|
158
270
|
<span>{authorName}</span>
|
|
159
271
|
<span>·</span>
|
|
160
272
|
<span>{formatDateTime(note.createdAt, timezone)}</span>
|
|
161
273
|
</div>
|
|
274
|
+
{deleteNote.error !== null && (
|
|
275
|
+
<div className="mt-2">
|
|
276
|
+
<ErrorPanel error={deleteNote.error} />
|
|
277
|
+
</div>
|
|
278
|
+
)}
|
|
162
279
|
</li>
|
|
163
280
|
)
|
|
164
281
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PLAN_ITEM_STATUS_LABELS, PLAN_ITEM_TARGET_TYPE_LABELS } from '@kelpie/schemas'
|
|
2
2
|
import type {
|
|
3
3
|
Deal,
|
|
4
4
|
Enquiry,
|
|
5
5
|
Opportunity,
|
|
6
6
|
Partnership,
|
|
7
|
-
PipelineKind,
|
|
8
7
|
PlanItem,
|
|
8
|
+
PlanItemTargetType,
|
|
9
9
|
Raise,
|
|
10
10
|
} from '@kelpie/schemas'
|
|
11
11
|
import { Link } from 'react-router'
|
|
@@ -27,12 +27,13 @@ import { SectionHeader } from './SectionHeader.tsx'
|
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
29
|
/** Where a plan item's record lives. */
|
|
30
|
-
const ROUTES: Readonly<Record<
|
|
30
|
+
const ROUTES: Readonly<Record<PlanItemTargetType, string | undefined>> = {
|
|
31
31
|
enquiry: '/enquiries',
|
|
32
32
|
deal: '/deals',
|
|
33
33
|
opportunity: '/opportunities',
|
|
34
34
|
raise: '/fundraising',
|
|
35
35
|
partnership: '/partnerships',
|
|
36
|
+
event: '/events',
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
export interface PlanAttentionProps {
|
|
@@ -247,7 +248,7 @@ export function PlanTargetLink({
|
|
|
247
248
|
readonly item: PlanItem
|
|
248
249
|
readonly targetNames: ReadonlyMap<string, string> | undefined
|
|
249
250
|
}): React.JSX.Element {
|
|
250
|
-
const kind =
|
|
251
|
+
const kind = PLAN_ITEM_TARGET_TYPE_LABELS[item.targetType]
|
|
251
252
|
const name = targetNames?.get(item.targetId)
|
|
252
253
|
const route = ROUTES[item.targetType]
|
|
253
254
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PLAN_ITEM_STATUS_LABELS, PLAN_ITEM_STATUSES } from '@kelpie/schemas'
|
|
2
|
-
import type {
|
|
2
|
+
import type { PlanItem, PlanItemStatus, PlanItemTargetType } from '@kelpie/schemas'
|
|
3
3
|
import { useState } from 'react'
|
|
4
4
|
import type { FormEvent } from 'react'
|
|
5
5
|
|
|
@@ -18,7 +18,7 @@ import { ErrorPanel } from './QueryState.tsx'
|
|
|
18
18
|
import { SectionHeader } from './SectionHeader.tsx'
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
|
-
* The plan for one
|
|
21
|
+
* The plan for one record: what happens next, when, and who has it.
|
|
22
22
|
*
|
|
23
23
|
* Ported from the mockup's panel, edit and delete included. Notes have neither,
|
|
24
24
|
* because the mockup's note panel offers neither; this one offers both, so both
|
|
@@ -30,7 +30,7 @@ import { SectionHeader } from './SectionHeader.tsx'
|
|
|
30
30
|
*/
|
|
31
31
|
|
|
32
32
|
export interface PlanPanelProps {
|
|
33
|
-
readonly targetType:
|
|
33
|
+
readonly targetType: PlanItemTargetType
|
|
34
34
|
readonly targetId: string
|
|
35
35
|
}
|
|
36
36
|
|
package/src/components/Shell.tsx
CHANGED
|
@@ -5,10 +5,12 @@ import { useAccount, useTheme } from '../api/resources/account.ts'
|
|
|
5
5
|
import { useLogOut } from '../api/resources/session.ts'
|
|
6
6
|
import { initialsOf } from '../lib/names.ts'
|
|
7
7
|
import type { ThemePreference } from '../lib/theme.ts'
|
|
8
|
+
import { ONBOARDING_RERUN_ENTRY } from '../pages/onboarding/onboardingRerun.ts'
|
|
8
9
|
import { useNavItems } from '../registry/context.ts'
|
|
9
10
|
import type { NavItem } from '../registry/contributions.ts'
|
|
10
11
|
import { useVisibleNavItems } from '../registry/visibleNav.ts'
|
|
11
12
|
import { ErrorBoundary } from './ErrorBoundary.tsx'
|
|
13
|
+
import { WorkspaceSwitcher } from './WorkspaceSwitcher.tsx'
|
|
12
14
|
|
|
13
15
|
/**
|
|
14
16
|
* Sidebar, header, and the outlet every page renders into.
|
|
@@ -40,6 +42,7 @@ const CORE_NAV: readonly NavItem[] = [
|
|
|
40
42
|
{ id: 'deals', label: 'Deals', to: '/deals', order: 300 },
|
|
41
43
|
{ id: 'opportunities', label: 'Opportunities', to: '/opportunities', order: 350 },
|
|
42
44
|
{ id: 'fundraising', label: 'Fundraising', to: '/fundraising', order: 360 },
|
|
45
|
+
{ id: 'events', label: 'Events', to: '/events', order: 370 },
|
|
43
46
|
{ id: 'partnerships', label: 'Partnerships', to: '/partnerships', order: 375 },
|
|
44
47
|
{ id: 'lists', label: 'Lists', to: '/lists', order: 378 },
|
|
45
48
|
{ id: 'forms', label: 'Forms', to: '/forms', order: 380 },
|
|
@@ -260,6 +263,7 @@ export function Shell(): React.JSX.Element {
|
|
|
260
263
|
>
|
|
261
264
|
{THEME_LABELS[theme]}
|
|
262
265
|
</button>
|
|
266
|
+
<WorkspaceSwitcher />
|
|
263
267
|
<div className="relative" ref={menuRef}>
|
|
264
268
|
<button
|
|
265
269
|
type="button"
|
|
@@ -302,10 +306,21 @@ export function Shell(): React.JSX.Element {
|
|
|
302
306
|
onClick={() => {
|
|
303
307
|
setMenuOpen(false)
|
|
304
308
|
}}
|
|
305
|
-
className="block
|
|
309
|
+
className="block px-3 py-1.5 text-left text-[13px] text-ink hover:bg-surface-sunken"
|
|
306
310
|
>
|
|
307
311
|
Preferences
|
|
308
312
|
</NavLink>
|
|
313
|
+
<button
|
|
314
|
+
type="button"
|
|
315
|
+
role="menuitem"
|
|
316
|
+
onClick={() => {
|
|
317
|
+
setMenuOpen(false)
|
|
318
|
+
void navigate(ONBOARDING_RERUN_ENTRY)
|
|
319
|
+
}}
|
|
320
|
+
className="block w-full border-b border-border px-3 py-1.5 text-left text-[13px] text-ink hover:bg-surface-sunken"
|
|
321
|
+
>
|
|
322
|
+
Rerun onboarding wizard
|
|
323
|
+
</button>
|
|
309
324
|
<button
|
|
310
325
|
type="button"
|
|
311
326
|
role="menuitem"
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { SOCIAL_NETWORK_LABELS } from '@kelpie/schemas'
|
|
2
|
+
import type { SocialNetworkId } from '@kelpie/schemas'
|
|
3
|
+
import {
|
|
4
|
+
siBluesky,
|
|
5
|
+
siCrunchbase,
|
|
6
|
+
siFacebook,
|
|
7
|
+
siGithub,
|
|
8
|
+
siInstagram,
|
|
9
|
+
siMastodon,
|
|
10
|
+
siMedium,
|
|
11
|
+
siSubstack,
|
|
12
|
+
siThreads,
|
|
13
|
+
siTiktok,
|
|
14
|
+
siWellfound,
|
|
15
|
+
siX,
|
|
16
|
+
siYoutube,
|
|
17
|
+
} from 'simple-icons'
|
|
18
|
+
|
|
19
|
+
export interface SocialNetworkIconProps {
|
|
20
|
+
readonly network: SocialNetworkId
|
|
21
|
+
readonly className?: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Simple Icons does not ship LinkedIn. This is the standard "in" mark used to
|
|
26
|
+
* identify a LinkedIn profile, not a substitute brand colour lockup.
|
|
27
|
+
*/
|
|
28
|
+
const LINKEDIN_PATH =
|
|
29
|
+
'M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z'
|
|
30
|
+
|
|
31
|
+
/** Generic link glyph for the catch-all network. Heroicons MIT, 24 solid. */
|
|
32
|
+
const OTHER_PATH =
|
|
33
|
+
'M12.232 4.232a2.5 2.5 0 0 1 3.536 3.536l-1.225 1.224a.75.75 0 0 0 1.061 1.06l1.224-1.224a4 4 0 0 0-5.656-5.656l-3 3a4 4 0 0 0 .225 5.865.75.75 0 0 0 .977-1.138 2.5 2.5 0 0 1-.142-3.667l3-3ZM11.768 19.768a2.5 2.5 0 0 1-3.536-3.536l1.225-1.224a.75.75 0 0 0-1.061-1.06l-1.224 1.224a4 4 0 1 0 5.656 5.656l3-3a4 4 0 0 0-.225-5.865.75.75 0 0 0-.977 1.138 2.5 2.5 0 0 1 .142 3.667l-3 3Z'
|
|
34
|
+
|
|
35
|
+
const ICON_PATHS: Readonly<Record<SocialNetworkId, string>> = {
|
|
36
|
+
angellist: siWellfound.path,
|
|
37
|
+
bluesky: siBluesky.path,
|
|
38
|
+
crunchbase: siCrunchbase.path,
|
|
39
|
+
facebook: siFacebook.path,
|
|
40
|
+
github: siGithub.path,
|
|
41
|
+
instagram: siInstagram.path,
|
|
42
|
+
linkedin: LINKEDIN_PATH,
|
|
43
|
+
mastodon: siMastodon.path,
|
|
44
|
+
medium: siMedium.path,
|
|
45
|
+
substack: siSubstack.path,
|
|
46
|
+
threads: siThreads.path,
|
|
47
|
+
tiktok: siTiktok.path,
|
|
48
|
+
twitter: siX.path,
|
|
49
|
+
youtube: siYoutube.path,
|
|
50
|
+
other: OTHER_PATH,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Brand mark for a social network. Decorative when the network name sits next
|
|
55
|
+
* to it; fill follows the surrounding text colour so light and dark both work.
|
|
56
|
+
*/
|
|
57
|
+
export function SocialNetworkIcon({
|
|
58
|
+
network,
|
|
59
|
+
className,
|
|
60
|
+
}: SocialNetworkIconProps): React.JSX.Element {
|
|
61
|
+
return (
|
|
62
|
+
<svg
|
|
63
|
+
viewBox="0 0 24 24"
|
|
64
|
+
aria-hidden="true"
|
|
65
|
+
className={['shrink-0', className ?? 'size-3.5'].join(' ')}
|
|
66
|
+
>
|
|
67
|
+
<path fill="currentColor" d={ICON_PATHS[network]} />
|
|
68
|
+
</svg>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface SocialNetworkNameProps {
|
|
73
|
+
readonly network: SocialNetworkId
|
|
74
|
+
readonly className?: string
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Icon plus the display name, for pickers and compact chips. */
|
|
78
|
+
export function SocialNetworkName({
|
|
79
|
+
network,
|
|
80
|
+
className,
|
|
81
|
+
}: SocialNetworkNameProps): React.JSX.Element {
|
|
82
|
+
return (
|
|
83
|
+
<span className={['inline-flex items-center gap-1.5', className ?? ''].join(' ')}>
|
|
84
|
+
<SocialNetworkIcon network={network} />
|
|
85
|
+
<span>{SOCIAL_NETWORK_LABELS[network]}</span>
|
|
86
|
+
</span>
|
|
87
|
+
)
|
|
88
|
+
}
|
|
@@ -2,6 +2,8 @@ import { SOCIAL_NETWORK_IDS, SOCIAL_NETWORK_LABELS } from '@kelpie/schemas'
|
|
|
2
2
|
import type { SocialNetworkId, SocialProfile } from '@kelpie/schemas'
|
|
3
3
|
import { useEffect, useId, useMemo, useRef, useState } from 'react'
|
|
4
4
|
|
|
5
|
+
import { SocialNetworkIcon, SocialNetworkName } from './SocialNetworkIcon.tsx'
|
|
6
|
+
|
|
5
7
|
export interface SocialProfilesFieldProps {
|
|
6
8
|
readonly value: readonly SocialProfile[]
|
|
7
9
|
readonly onChange: (profiles: readonly SocialProfile[]) => void
|
|
@@ -25,6 +27,45 @@ function profileHandle(url: string): string {
|
|
|
25
27
|
return path.length > 0 ? path : bare
|
|
26
28
|
}
|
|
27
29
|
|
|
30
|
+
export interface SocialProfileIconsProps {
|
|
31
|
+
readonly profiles: readonly SocialProfile[]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Compact outbound marks for the person heading. Empty when there are no
|
|
36
|
+
* profiles; the Social tab is the editor.
|
|
37
|
+
*/
|
|
38
|
+
export function SocialProfileIcons({
|
|
39
|
+
profiles,
|
|
40
|
+
}: SocialProfileIconsProps): React.JSX.Element | null {
|
|
41
|
+
if (profiles.length === 0) {
|
|
42
|
+
return null
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<ul className="flex shrink-0 items-center gap-1.5">
|
|
47
|
+
{profiles.map((profile) => {
|
|
48
|
+
const label = SOCIAL_NETWORK_LABELS[profile.network]
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<li key={profile.network}>
|
|
52
|
+
<a
|
|
53
|
+
href={hrefFor(profile.url)}
|
|
54
|
+
target="_blank"
|
|
55
|
+
rel="noopener noreferrer"
|
|
56
|
+
title={`${label} · ${profileHandle(profile.url)}`}
|
|
57
|
+
aria-label={label}
|
|
58
|
+
className="inline-flex text-ink-muted transition hover:text-accent"
|
|
59
|
+
>
|
|
60
|
+
<SocialNetworkIcon network={profile.network} className="size-4" />
|
|
61
|
+
</a>
|
|
62
|
+
</li>
|
|
63
|
+
)
|
|
64
|
+
})}
|
|
65
|
+
</ul>
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
|
|
28
69
|
/** One profile per network, so the network list doubles as the set of things left to add. */
|
|
29
70
|
export function SocialProfilesField({
|
|
30
71
|
value,
|
|
@@ -150,7 +191,9 @@ export function SocialProfilesField({
|
|
|
150
191
|
if (editingNetwork === profile.network) {
|
|
151
192
|
return (
|
|
152
193
|
<li key={profile.network} className="py-1">
|
|
153
|
-
<div className="mb-1
|
|
194
|
+
<div className="mb-1">
|
|
195
|
+
<SocialNetworkName network={profile.network} className="text-[11px] font-medium text-ink-muted" />
|
|
196
|
+
</div>
|
|
154
197
|
<input
|
|
155
198
|
ref={editInputRef}
|
|
156
199
|
type="url"
|
|
@@ -186,11 +229,14 @@ export function SocialProfilesField({
|
|
|
186
229
|
target="_blank"
|
|
187
230
|
rel="noopener noreferrer"
|
|
188
231
|
title={profile.url}
|
|
189
|
-
className="min-w-0 flex-1
|
|
232
|
+
className="flex min-w-0 flex-1 items-center gap-1.5 text-[12px] leading-snug text-ink hover:text-accent"
|
|
190
233
|
>
|
|
191
|
-
<
|
|
192
|
-
<span className="
|
|
193
|
-
|
|
234
|
+
<SocialNetworkIcon network={profile.network} />
|
|
235
|
+
<span className="min-w-0 truncate">
|
|
236
|
+
<span className="font-medium">{label}</span>
|
|
237
|
+
<span className="text-ink-faint"> · </span>
|
|
238
|
+
<span className="text-ink-muted">{profileHandle(profile.url)}</span>
|
|
239
|
+
</span>
|
|
194
240
|
</a>
|
|
195
241
|
<div className="flex shrink-0 items-center">
|
|
196
242
|
<button
|
|
@@ -230,9 +276,10 @@ export function SocialProfilesField({
|
|
|
230
276
|
|
|
231
277
|
{pendingNetwork !== null ? (
|
|
232
278
|
<div className="mt-1 space-y-1.5 rounded-md border border-border bg-surface px-2 py-2">
|
|
233
|
-
<
|
|
234
|
-
{
|
|
235
|
-
|
|
279
|
+
<SocialNetworkName
|
|
280
|
+
network={pendingNetwork}
|
|
281
|
+
className="text-[11px] font-medium text-ink"
|
|
282
|
+
/>
|
|
236
283
|
<input
|
|
237
284
|
ref={urlInputRef}
|
|
238
285
|
type="url"
|
|
@@ -336,11 +383,11 @@ export function SocialProfilesField({
|
|
|
336
383
|
pickNetwork(network)
|
|
337
384
|
}}
|
|
338
385
|
className={[
|
|
339
|
-
'flex w-full px-3 py-1.5 text-left text-[12px] text-ink',
|
|
386
|
+
'flex w-full items-center px-3 py-1.5 text-left text-[12px] text-ink',
|
|
340
387
|
index === highlight ? 'bg-accent-soft' : 'hover:bg-surface',
|
|
341
388
|
].join(' ')}
|
|
342
389
|
>
|
|
343
|
-
{
|
|
390
|
+
<SocialNetworkName network={network} />
|
|
344
391
|
</button>
|
|
345
392
|
</li>
|
|
346
393
|
))
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { JSX } from 'react'
|
|
2
|
+
|
|
3
|
+
import { timezonesIncluding } from '../lib/timezones.ts'
|
|
4
|
+
|
|
5
|
+
import { EntitySearch } from './EntitySearch.tsx'
|
|
6
|
+
|
|
7
|
+
export interface TimezoneSearchProps {
|
|
8
|
+
readonly value: string
|
|
9
|
+
readonly onChange: (timezone: string) => void
|
|
10
|
+
readonly required?: boolean | undefined
|
|
11
|
+
readonly disabled?: boolean | undefined
|
|
12
|
+
readonly size?: 'sm' | 'md' | undefined
|
|
13
|
+
readonly className?: string | undefined
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Searchable IANA timezone picker.
|
|
18
|
+
*
|
|
19
|
+
* The person sidebar used to be a free-text InlineEdit, which accepted anything
|
|
20
|
+
* and offered no list. Account preferences already searched the full ICU set;
|
|
21
|
+
* this is that control, shared with Events and workspace settings.
|
|
22
|
+
*
|
|
23
|
+
* Clearing a required field is ignored: the stored value stays until a real
|
|
24
|
+
* zone is picked, so an Event or workspace cannot PATCH an empty string.
|
|
25
|
+
*/
|
|
26
|
+
export function TimezoneSearch({
|
|
27
|
+
value,
|
|
28
|
+
onChange,
|
|
29
|
+
required,
|
|
30
|
+
disabled,
|
|
31
|
+
size = 'sm',
|
|
32
|
+
className = '',
|
|
33
|
+
}: TimezoneSearchProps): JSX.Element {
|
|
34
|
+
const options = timezonesIncluding(value).map((zone) => ({ id: zone, label: zone }))
|
|
35
|
+
|
|
36
|
+
if (disabled === true) {
|
|
37
|
+
const padding = size === 'md' ? 'px-2.5 py-1.5 text-[13px]' : 'px-2 py-1.5 text-[12px]'
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<div
|
|
41
|
+
className={`w-full rounded-md border border-border bg-surface-raised text-ink opacity-60 ${padding} ${className}`}
|
|
42
|
+
>
|
|
43
|
+
{value.length > 0 ? value : '—'}
|
|
44
|
+
</div>
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<EntitySearch
|
|
50
|
+
options={options}
|
|
51
|
+
value={value}
|
|
52
|
+
onChange={(next) => {
|
|
53
|
+
if (required === true && next.length === 0) {
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
onChange(next)
|
|
58
|
+
}}
|
|
59
|
+
placeholder="Search time zones…"
|
|
60
|
+
emptyMessage="No matching time zones"
|
|
61
|
+
limit={20}
|
|
62
|
+
required={required}
|
|
63
|
+
size={size}
|
|
64
|
+
className={className}
|
|
65
|
+
/>
|
|
66
|
+
)
|
|
67
|
+
}
|