@hyvor/design 1.1.25-beta-org-2 → 1.1.25-beta-org-4

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.
@@ -4,6 +4,7 @@
4
4
  import BarProducts, { PRODUCTS } from './BarProducts.svelte';
5
5
  import BarSupport from './BarSupport.svelte';
6
6
  import {
7
+ barOnOrganizationSwitch,
7
8
  barUser,
8
9
  initBar,
9
10
  setInstanceAndProduct,
@@ -71,13 +72,13 @@
71
72
  }
72
73
  }
73
74
 
75
+ barOnOrganizationSwitch.set(onOrganizationSwitch);
76
+ setInstanceAndProduct(instance, product);
74
77
  if (authOverride) {
75
78
  barUser.set(authOverride.user);
76
79
  }
77
80
 
78
81
  onMount(() => {
79
- setInstanceAndProduct(instance, product);
80
-
81
82
  if (cloud) {
82
83
  initBar();
83
84
  }
@@ -102,7 +103,7 @@
102
103
  {getName()}
103
104
  </span>
104
105
  </a>
105
- <BarOrganization onSwitch={onOrganizationSwitch} />
106
+ <BarOrganization />
106
107
  <BarLicense name={getName()} />
107
108
  </div>
108
109
  <div class="right">
@@ -1,13 +1,17 @@
1
1
  <script lang="ts">
2
2
  import Tooltip from '../../Tooltip/Tooltip.svelte';
3
- import { barOrganizationDropdownOpen, barUser, type BarOrganization } from '../bar.js';
3
+ import {
4
+ barOrganizationCreating,
5
+ barOrganizationDropdownOpen,
6
+ barUser,
7
+ type BarOrganization
8
+ } from '../bar.js';
9
+ import CreateOrganization from './CreateOrganization.svelte';
4
10
  import OrganizationButton from './OrganizationButton.svelte';
5
11
 
6
12
  let disableTooltip = $state(false);
7
13
 
8
- let props: {
9
- onSwitch: (org: BarOrganization) => void;
10
- } = $props();
14
+ let props: {} = $props();
11
15
 
12
16
  barOrganizationDropdownOpen.subscribe((value) => {
13
17
  if (value) disableTooltip = true;
@@ -17,15 +21,13 @@
17
21
  {#if $barUser}
18
22
  <Tooltip position="bottom" text="Switch Organization" disabled={disableTooltip}>
19
23
  <div class="wrap">
20
- <OrganizationButton
21
- bind:show={$barOrganizationDropdownOpen}
22
- onSwitch={props.onSwitch}
23
- style="bordered"
24
- />
24
+ <OrganizationButton bind:show={$barOrganizationDropdownOpen} style="bordered" />
25
25
  </div>
26
26
  </Tooltip>
27
27
  {/if}
28
28
 
29
+ <CreateOrganization bind:show={$barOrganizationCreating} />
30
+
29
31
  <style>
30
32
  .wrap {
31
33
  height: 100%;
@@ -1,7 +1,5 @@
1
1
  import { type BarOrganization } from '../bar.js';
2
- type $$ComponentProps = {
3
- onSwitch: (org: BarOrganization) => void;
4
- };
2
+ type $$ComponentProps = {};
5
3
  declare const BarOrganization: import("svelte").Component<$$ComponentProps, {}, "">;
6
4
  type BarOrganization = ReturnType<typeof BarOrganization>;
7
5
  export default BarOrganization;
@@ -0,0 +1,84 @@
1
+ <script lang="ts">
2
+ import Modal from '../../Modal/Modal.svelte';
3
+ import SplitControl from '../../SplitControl/SplitControl.svelte';
4
+ import TextInput from '../../TextInput/TextInput.svelte';
5
+ import toast from '../../Toast/toast.ts';
6
+ import {
7
+ barOnOrganizationSwitch,
8
+ createOrganization,
9
+ switchOrganization,
10
+ type BarOrganization
11
+ } from '../bar.ts';
12
+
13
+ interface Props {
14
+ show?: boolean;
15
+ }
16
+
17
+ let { show = $bindable(false) }: Props = $props();
18
+
19
+ let name = $state('');
20
+ let input: HTMLInputElement | undefined = $state(undefined);
21
+ let creating = $state(false);
22
+
23
+ async function handleCreate() {
24
+ if (name.trim() === '') {
25
+ toast.error('Organization name cannot be empty.');
26
+ return;
27
+ }
28
+
29
+ creating = true;
30
+
31
+ let org: BarOrganization;
32
+
33
+ try {
34
+ org = await createOrganization(name);
35
+ } catch {
36
+ toast.error('Failed to create organization.');
37
+ creating = false;
38
+ return;
39
+ }
40
+
41
+ show = false;
42
+ name = '';
43
+
44
+ try {
45
+ await switchOrganization(org.id);
46
+ $barOnOrganizationSwitch?.(org);
47
+ } catch {
48
+ toast.error('Failed to switch to the new organization.');
49
+ return;
50
+ } finally {
51
+ creating = false;
52
+ }
53
+ }
54
+
55
+ $effect(() => {
56
+ if (show && input) {
57
+ input.focus();
58
+ }
59
+ });
60
+ </script>
61
+
62
+ <Modal
63
+ bind:show
64
+ title="Create Organization"
65
+ size="small"
66
+ footer={{ confirm: { text: 'Create' }, cancel: { text: 'Cancel' } }}
67
+ on:confirm={handleCreate}
68
+ loading={creating}
69
+ >
70
+ <SplitControl label="Name" column>
71
+ <TextInput
72
+ name="organizationName"
73
+ bind:value={name}
74
+ placeholder="Organization Name"
75
+ block
76
+ onkeyup={(e) => {
77
+ if (e.key === 'Enter') {
78
+ handleCreate();
79
+ }
80
+ }}
81
+ bind:input
82
+ />
83
+ </SplitControl>
84
+ </Modal>
@@ -0,0 +1,6 @@
1
+ interface Props {
2
+ show?: boolean;
3
+ }
4
+ declare const CreateOrganization: import("svelte").Component<Props, {}, "show">;
5
+ type CreateOrganization = ReturnType<typeof CreateOrganization>;
6
+ export default CreateOrganization;
@@ -2,7 +2,12 @@
2
2
  import Dropdown from '../../Dropdown/Dropdown.svelte';
3
3
  import IconChevronExpand from '@hyvor/icons/IconChevronExpand';
4
4
  import OrgsList from './OrgsList.svelte';
5
- import { barUser, switchOrganization, type BarOrganization } from '../bar.js';
5
+ import {
6
+ barOnOrganizationSwitch,
7
+ barUser,
8
+ switchOrganization,
9
+ type BarOrganization
10
+ } from '../bar.js';
6
11
  import toast from '../../Toast/toast.js';
7
12
  import type { DropdownAlign } from '../../Dropdown/dropdown.types.js';
8
13
 
@@ -14,17 +19,11 @@
14
19
 
15
20
  interface Props {
16
21
  show?: boolean;
17
- onSwitch?: (org: BarOrganization) => void;
18
22
  style?: 'bordered' | 'plain';
19
23
  dropdownAlign?: DropdownAlign;
20
24
  }
21
25
 
22
- let {
23
- show = $bindable(false),
24
- onSwitch,
25
- style = 'plain',
26
- dropdownAlign = 'center'
27
- }: Props = $props();
26
+ let { show = $bindable(false), style = 'plain', dropdownAlign = 'center' }: Props = $props();
28
27
  let switching = $state(false);
29
28
 
30
29
  async function handleSwitch(org: BarOrganization) {
@@ -32,15 +31,19 @@
32
31
  switching = true;
33
32
 
34
33
  try {
35
- await switchOrganization(org);
34
+ await switchOrganization(org.id);
36
35
  } catch (e) {
37
36
  toast.error('Failed to switch organization.');
38
- switching = false;
39
37
  return;
38
+ } finally {
39
+ switching = false;
40
40
  }
41
41
 
42
- switching = false;
43
- onSwitch?.(org);
42
+ $barOnOrganizationSwitch?.(org);
43
+ }
44
+
45
+ function handleCreateOrgStart() {
46
+ show = false;
44
47
  }
45
48
  </script>
46
49
 
@@ -57,7 +60,7 @@
57
60
  </button>
58
61
  {/snippet}
59
62
  {#snippet content()}
60
- <OrgsList onSwitch={handleSwitch} />
63
+ <OrgsList onSwitch={handleSwitch} onCreateStart={handleCreateOrgStart} />
61
64
  {/snippet}
62
65
  </Dropdown>
63
66
  {/if}
@@ -1,4 +1,3 @@
1
- import { type BarOrganization } from '../bar.js';
2
1
  import type { DropdownAlign } from '../../Dropdown/dropdown.types.js';
3
2
  /**
4
3
  * This component depends on data from HyvorBar
@@ -7,7 +6,6 @@ import type { DropdownAlign } from '../../Dropdown/dropdown.types.js';
7
6
  */
8
7
  interface Props {
9
8
  show?: boolean;
10
- onSwitch?: (org: BarOrganization) => void;
11
9
  style?: 'bordered' | 'plain';
12
10
  dropdownAlign?: DropdownAlign;
13
11
  }
@@ -2,17 +2,28 @@
2
2
  import IconMessage from '../../IconMessage/IconMessage.svelte';
3
3
  import Loader from '../../Loader/Loader.svelte';
4
4
  import { onMount } from 'svelte';
5
- import { barOrganizations, getMyOrganizations, type BarOrganization } from '../bar.js';
5
+ import {
6
+ barOrganizationCreating,
7
+ barOrganizations,
8
+ getMyOrganizations,
9
+ instance,
10
+ type BarOrganization
11
+ } from '../bar.js';
6
12
  import ActionList from '../../ActionList/ActionList.svelte';
7
13
  import ActionListItem from '../../ActionList/ActionListItem.svelte';
14
+ import Button from '../../Button/Button.svelte';
15
+ import IconPlus from '@hyvor/icons/IconPlus';
16
+ import IconBoxArrowUpRight from '@hyvor/icons/IconBoxArrowUpRight';
8
17
 
9
18
  let loading = $state(true);
10
19
  let error = $state('');
11
20
 
12
21
  let {
13
- onSwitch
22
+ onSwitch,
23
+ onCreateStart
14
24
  }: {
15
25
  onSwitch: (org: BarOrganization) => void;
26
+ onCreateStart: () => void;
16
27
  } = $props();
17
28
 
18
29
  onMount(() => {
@@ -20,6 +31,7 @@
20
31
  loading = false;
21
32
  return;
22
33
  }
34
+
23
35
  getMyOrganizations()
24
36
  .then((data) => {
25
37
  barOrganizations.set(data);
@@ -31,6 +43,11 @@
31
43
  loading = false;
32
44
  });
33
45
  });
46
+
47
+ function handleCreate() {
48
+ barOrganizationCreating.set(true);
49
+ onCreateStart();
50
+ }
34
51
  </script>
35
52
 
36
53
  {#if loading}
@@ -54,6 +71,28 @@
54
71
  {/each}
55
72
  </ActionList>
56
73
  </div>
74
+
75
+ <div class="manage-create">
76
+ <Button size="small" onclick={handleCreate}>
77
+ Create
78
+ {#snippet end()}
79
+ <IconPlus />
80
+ {/snippet}
81
+ </Button>
82
+ <Button
83
+ size="small"
84
+ as="a"
85
+ href={$instance + '/account/org'}
86
+ color="input"
87
+ target="_blank"
88
+ rel="noopener noreferrer"
89
+ >
90
+ Manage
91
+ {#snippet end()}
92
+ <IconBoxArrowUpRight size={12} />
93
+ {/snippet}
94
+ </Button>
95
+ </div>
57
96
  {/if}
58
97
 
59
98
  <style>
@@ -67,4 +106,15 @@
67
106
  overflow-y: auto;
68
107
  padding: 10px;
69
108
  }
109
+ .manage-create {
110
+ border-top: 1px solid var(--border);
111
+ padding: 10px;
112
+ display: flex;
113
+ gap: 10px;
114
+ align-items: center;
115
+ justify-content: center;
116
+ }
117
+ .manage-create :global(a) {
118
+ text-decoration: none !important;
119
+ }
70
120
  </style>
@@ -1,6 +1,7 @@
1
1
  import { type BarOrganization } from '../bar.js';
2
2
  type $$ComponentProps = {
3
3
  onSwitch: (org: BarOrganization) => void;
4
+ onCreateStart: () => void;
4
5
  };
5
6
  declare const OrgsList: import("svelte").Component<$$ComponentProps, {}, "">;
6
7
  type OrgsList = ReturnType<typeof OrgsList>;
@@ -35,19 +35,24 @@ export interface BarResolvedLicense {
35
35
  };
36
36
  trial_ends_at: null | number;
37
37
  }
38
+ export declare const instance: import("svelte/store").Writable<string>;
38
39
  export declare const barUser: import("svelte/store").Writable<BarUser | null>;
39
40
  export declare const barUnreadUpdates: import("svelte/store").Writable<number>;
40
41
  export declare const barLicense: import("svelte/store").Writable<BarResolvedLicense | null>;
41
42
  export declare const barHasFailedInvoices: import("svelte/store").Writable<boolean>;
42
43
  export declare const barOrganizationDropdownOpen: import("svelte/store").Writable<boolean>;
43
44
  export declare const barOrganizations: import("svelte/store").Writable<BarOrganization[]>;
45
+ export declare const barOnOrganizationSwitch: import("svelte/store").Writable<((org: BarOrganization) => void) | null>;
46
+ export declare const barOrganizationCreating: import("svelte/store").Writable<boolean>;
44
47
  export declare function setInstanceAndProduct(instance_: string, product_: string): void;
48
+ export declare function getInstance(): string;
45
49
  /**
46
50
  * @throws Error if initialization fails
47
51
  */
48
52
  export declare function initBar(): Promise<void>;
49
53
  export declare function getMyOrganizations(): Promise<BarOrganization[]>;
50
- export declare function switchOrganization(org: BarOrganization): Promise<void>;
54
+ export declare function createOrganization(name: string): Promise<BarOrganization>;
55
+ export declare function switchOrganization(orgId: number): Promise<void>;
51
56
  export declare class UnreadUpdatesTimeLocalStorage {
52
57
  static KEY: string;
53
58
  static get(): number | null;
@@ -1,17 +1,22 @@
1
1
  import { track } from '../../marketing/index.js';
2
- import { writable } from 'svelte/store';
3
- let instance = '';
2
+ import { get, writable } from 'svelte/store';
4
3
  let product = 'core';
4
+ export const instance = writable('');
5
5
  export const barUser = writable(null);
6
6
  export const barUnreadUpdates = writable(0);
7
7
  export const barLicense = writable(null);
8
8
  export const barHasFailedInvoices = writable(false);
9
9
  export const barOrganizationDropdownOpen = writable(false);
10
10
  export const barOrganizations = writable([]);
11
+ export const barOnOrganizationSwitch = writable(null);
12
+ export const barOrganizationCreating = writable(false);
11
13
  export function setInstanceAndProduct(instance_, product_) {
12
- instance = instance_;
14
+ instance.set(instance_);
13
15
  product = product_;
14
16
  }
17
+ export function getInstance() {
18
+ return get(instance);
19
+ }
15
20
  /**
16
21
  * @throws Error if initialization fails
17
22
  */
@@ -22,7 +27,7 @@ export async function initBar() {
22
27
  if (lastUnreadTime) {
23
28
  query.set('last_read_updates_at', lastUnreadTime.toString());
24
29
  }
25
- const response = await fetch(instance + '/api/public/bar?' + query.toString(), {
30
+ const response = await fetch(getInstance() + '/api/public/bar?' + query.toString(), {
26
31
  credentials: 'include'
27
32
  });
28
33
  if (!response.ok) {
@@ -39,7 +44,7 @@ export async function initBar() {
39
44
  if (data.user && track.ready()) {
40
45
  track.identify(data.user.id.toString(), {
41
46
  name: data.user.name ?? undefined,
42
- avatar: data.user.picture_url ?? undefined,
47
+ avatar: data.user.picture_url ?? undefined
43
48
  });
44
49
  }
45
50
  }
@@ -48,22 +53,37 @@ export async function getMyOrganizations() {
48
53
  { id: 1, name: 'Org 1', role: 'admin' },
49
54
  { id: 2, name: 'Org 2', role: 'member' },
50
55
  { id: 3, name: 'Org 3', role: 'billing' },
51
- { id: 4, name: 'Org 4', role: 'manager' },
52
- ] */
53
- const response = await fetch(instance + '/api/public/bar/myorgs', {
56
+ { id: 4, name: 'Org 4', role: 'manager' }
57
+ ]; */
58
+ const response = await fetch(getInstance() + '/api/public/bar/orgs/my', {
54
59
  credentials: 'include'
55
60
  });
56
61
  const data = await response.json();
57
62
  return data.organizations;
58
63
  }
59
- export async function switchOrganization(org) {
60
- const response = await fetch(instance + '/api/public/bar/switch-org', {
64
+ export async function createOrganization(name) {
65
+ const response = await fetch(getInstance() + '/api/public/bar/orgs', {
66
+ method: 'POST',
67
+ credentials: 'include',
68
+ headers: {
69
+ 'Content-Type': 'application/json'
70
+ },
71
+ body: JSON.stringify({ name })
72
+ });
73
+ if (!response.ok) {
74
+ throw new Error('Failed to create organization');
75
+ }
76
+ const data = await response.json();
77
+ return data;
78
+ }
79
+ export async function switchOrganization(orgId) {
80
+ const response = await fetch(getInstance() + '/api/public/bar/orgs/switch', {
61
81
  method: 'POST',
62
82
  credentials: 'include',
63
83
  headers: {
64
84
  'Content-Type': 'application/json'
65
85
  },
66
- body: JSON.stringify({ organization_id: org.id })
86
+ body: JSON.stringify({ organization_id: orgId })
67
87
  });
68
88
  if (!response.ok) {
69
89
  throw new Error('Failed to switch organization');
package/package.json CHANGED
@@ -62,5 +62,5 @@
62
62
  "publishConfig": {
63
63
  "access": "public"
64
64
  },
65
- "version": "1.1.25-beta-org-2"
65
+ "version": "1.1.25-beta-org-4"
66
66
  }