@hyvor/design 1.1.24-beta.2 → 1.1.24-beta.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.
- package/dist/components/HyvorBar/HyvorBar.svelte +16 -23
- package/dist/components/HyvorBar/HyvorBar.svelte.d.ts +0 -2
- package/dist/components/HyvorBar/Notice/BarLicense.svelte +4 -2
- package/dist/components/HyvorBar/bar.d.ts +2 -19
- package/dist/components/HyvorBar/bar.js +22 -56
- package/dist/marketing/Header/Header.svelte +131 -140
- package/dist/marketing/Header/Header.svelte.d.ts +3 -1
- package/dist/marketing/Header/HeaderNotification.svelte +64 -0
- package/dist/marketing/Header/HeaderNotification.svelte.d.ts +7 -0
- package/dist/marketing/cloud.d.ts +1 -0
- package/dist/marketing/cloud.js +15 -0
- package/dist/marketing/track/track.d.ts +0 -1
- package/dist/marketing/track/track.js +3 -6
- package/package.json +1 -1
- package/dist/components/HyvorBar/Organization/BarOrganization.svelte +0 -111
- package/dist/components/HyvorBar/Organization/BarOrganization.svelte.d.ts +0 -7
- package/dist/components/HyvorBar/Organization/OrgsList.svelte +0 -70
- package/dist/components/HyvorBar/Organization/OrgsList.svelte.d.ts +0 -7
|
@@ -3,19 +3,11 @@
|
|
|
3
3
|
import { onMount } from 'svelte';
|
|
4
4
|
import BarProducts, { PRODUCTS } from './BarProducts.svelte';
|
|
5
5
|
import BarSupport from './BarSupport.svelte';
|
|
6
|
-
import {
|
|
7
|
-
barUser,
|
|
8
|
-
initBar,
|
|
9
|
-
setInstanceAndProduct,
|
|
10
|
-
type BarConfig,
|
|
11
|
-
type BarUser as BarUserType
|
|
12
|
-
} from './bar.js';
|
|
6
|
+
import { barUser, initBar, setInstanceAndProduct, type BarConfig, type BarUser as BarUserType } from './bar.js';
|
|
13
7
|
import BarUpdates from './BarUpdates.svelte';
|
|
14
8
|
import IconCaretDownFill from '@hyvor/icons/IconCaretDownFill';
|
|
15
9
|
import BarNotice from './Notice/BarNotice.svelte';
|
|
16
10
|
import BarLicense from './Notice/BarLicense.svelte';
|
|
17
|
-
import BarOrganization from './Organization/BarOrganization.svelte';
|
|
18
|
-
import { type BarOrganization as BarOrganizationType } from './bar.js';
|
|
19
11
|
|
|
20
12
|
interface Props {
|
|
21
13
|
instance?: string;
|
|
@@ -35,18 +27,16 @@
|
|
|
35
27
|
authOverride?: {
|
|
36
28
|
user: BarUserType | null;
|
|
37
29
|
logoutUrl: string;
|
|
38
|
-
}
|
|
39
|
-
onOrganizationSwitch?: (org: BarOrganizationType) => void;
|
|
30
|
+
}
|
|
40
31
|
}
|
|
41
32
|
|
|
42
33
|
let {
|
|
43
|
-
instance = 'https://hyvor.com',
|
|
44
|
-
product,
|
|
34
|
+
instance = 'https://hyvor.com',
|
|
35
|
+
product,
|
|
45
36
|
logo = `${instance}/api/public/logo/${product}.svg`,
|
|
46
37
|
config = {},
|
|
47
38
|
cloud = true,
|
|
48
|
-
authOverride = undefined
|
|
49
|
-
onOrganizationSwitch = () => {}
|
|
39
|
+
authOverride = undefined
|
|
50
40
|
}: Props = $props();
|
|
51
41
|
|
|
52
42
|
let mobileShow = $state(false);
|
|
@@ -71,15 +61,15 @@
|
|
|
71
61
|
}
|
|
72
62
|
}
|
|
73
63
|
|
|
74
|
-
if (authOverride) {
|
|
75
|
-
barUser.set(authOverride.user);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
64
|
onMount(() => {
|
|
79
65
|
setInstanceAndProduct(instance, product);
|
|
80
66
|
|
|
81
67
|
if (cloud) {
|
|
82
68
|
initBar();
|
|
69
|
+
} else {
|
|
70
|
+
if (authOverride) {
|
|
71
|
+
barUser.set(authOverride.user);
|
|
72
|
+
}
|
|
83
73
|
}
|
|
84
74
|
});
|
|
85
75
|
|
|
@@ -97,12 +87,16 @@
|
|
|
97
87
|
<div class="inner hds-box">
|
|
98
88
|
<div class="left">
|
|
99
89
|
<a class="logo" href="/">
|
|
100
|
-
<img
|
|
90
|
+
<img
|
|
91
|
+
src={logo}
|
|
92
|
+
alt={product}
|
|
93
|
+
width="20"
|
|
94
|
+
height="20"
|
|
95
|
+
/>
|
|
101
96
|
<span class="name">
|
|
102
97
|
{getName()}
|
|
103
98
|
</span>
|
|
104
99
|
</a>
|
|
105
|
-
<BarOrganization onSwitch={onOrganizationSwitch} />
|
|
106
100
|
<BarLicense name={getName()} />
|
|
107
101
|
</div>
|
|
108
102
|
<div class="right">
|
|
@@ -138,7 +132,7 @@
|
|
|
138
132
|
z-index: 100;
|
|
139
133
|
}
|
|
140
134
|
.inner {
|
|
141
|
-
padding:
|
|
135
|
+
padding: 10px 29px;
|
|
142
136
|
display: flex;
|
|
143
137
|
align-items: center;
|
|
144
138
|
border-top-left-radius: 0;
|
|
@@ -149,7 +143,6 @@
|
|
|
149
143
|
display: flex;
|
|
150
144
|
align-items: center;
|
|
151
145
|
flex: 1;
|
|
152
|
-
height: 100%;
|
|
153
146
|
}
|
|
154
147
|
.logo {
|
|
155
148
|
display: flex;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { type BarConfig, type BarUser as BarUserType } from './bar.js';
|
|
2
|
-
import { type BarOrganization as BarOrganizationType } from './bar.js';
|
|
3
2
|
interface Props {
|
|
4
3
|
instance?: string;
|
|
5
4
|
product: string;
|
|
@@ -14,7 +13,6 @@ interface Props {
|
|
|
14
13
|
user: BarUserType | null;
|
|
15
14
|
logoutUrl: string;
|
|
16
15
|
};
|
|
17
|
-
onOrganizationSwitch?: (org: BarOrganizationType) => void;
|
|
18
16
|
}
|
|
19
17
|
declare const HyvorBar: import("svelte").Component<Props, {}, "">;
|
|
20
18
|
type HyvorBar = ReturnType<typeof HyvorBar>;
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
return daysDiff(endsAt);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
function remainingCancelAtDAys(cancelAt: number | undefined | null): null | number {
|
|
25
|
+
function remainingCancelAtDAys(cancelAt: number | undefined | null) : null | number {
|
|
26
26
|
if (!cancelAt) {
|
|
27
27
|
return null;
|
|
28
28
|
}
|
|
@@ -36,7 +36,9 @@
|
|
|
36
36
|
{#if $barLicense}
|
|
37
37
|
<a class="wrap" href="/console/billing">
|
|
38
38
|
{#if $barLicense.type === 'subscription'}
|
|
39
|
-
<Tooltip
|
|
39
|
+
<Tooltip
|
|
40
|
+
position="bottom"
|
|
41
|
+
>
|
|
40
42
|
{#snippet tooltip()}
|
|
41
43
|
Your current subscription plan for {name}. Click to manage it.
|
|
42
44
|
{/snippet}
|
|
@@ -10,12 +10,6 @@ export interface BarUser {
|
|
|
10
10
|
username?: string | null;
|
|
11
11
|
email: string;
|
|
12
12
|
picture_url: string | null;
|
|
13
|
-
current_organization_name: string;
|
|
14
|
-
}
|
|
15
|
-
export interface BarOrganization {
|
|
16
|
-
id: number;
|
|
17
|
-
name: string;
|
|
18
|
-
role: 'admin' | 'billing' | 'manager' | 'member';
|
|
19
13
|
}
|
|
20
14
|
export interface BarUpdate {
|
|
21
15
|
id: number;
|
|
@@ -25,7 +19,7 @@ export interface BarUpdate {
|
|
|
25
19
|
content: string;
|
|
26
20
|
url?: string;
|
|
27
21
|
}
|
|
28
|
-
export type BarUpdateType = 'company' | 'core' | 'talk' | 'blogs' | 'fortguard';
|
|
22
|
+
export type BarUpdateType = 'company' | 'core' | 'talk' | 'blogs' | 'post' | 'relay' | 'fortguard';
|
|
29
23
|
export interface BarResolvedLicense {
|
|
30
24
|
type: 'subscription' | 'trial' | 'custom' | 'expired';
|
|
31
25
|
license: Record<string, number | boolean> | null;
|
|
@@ -39,15 +33,8 @@ export declare const barUser: import("svelte/store").Writable<BarUser | null>;
|
|
|
39
33
|
export declare const barUnreadUpdates: import("svelte/store").Writable<number>;
|
|
40
34
|
export declare const barLicense: import("svelte/store").Writable<BarResolvedLicense | null>;
|
|
41
35
|
export declare const barHasFailedInvoices: import("svelte/store").Writable<boolean>;
|
|
42
|
-
export declare const barOrganizationDropdownOpen: import("svelte/store").Writable<boolean>;
|
|
43
|
-
export declare const barOrganizations: import("svelte/store").Writable<BarOrganization[]>;
|
|
44
36
|
export declare function setInstanceAndProduct(instance_: string, product_: string): void;
|
|
45
|
-
|
|
46
|
-
* @throws Error if initialization fails
|
|
47
|
-
*/
|
|
48
|
-
export declare function initBar(): Promise<void>;
|
|
49
|
-
export declare function getMyOrganizations(): Promise<BarOrganization[]>;
|
|
50
|
-
export declare function switchOrganization(org: BarOrganization): Promise<void>;
|
|
37
|
+
export declare function initBar(): void;
|
|
51
38
|
export declare class UnreadUpdatesTimeLocalStorage {
|
|
52
39
|
static KEY: string;
|
|
53
40
|
static get(): number | null;
|
|
@@ -61,8 +48,4 @@ export declare const bar: {
|
|
|
61
48
|
* This is useful to create after, for example, a user creates a new blog
|
|
62
49
|
*/
|
|
63
50
|
reload: () => void;
|
|
64
|
-
/**
|
|
65
|
-
* Open the org selector dropdown from the outside world
|
|
66
|
-
*/
|
|
67
|
-
openOrganizationDropdown: () => void;
|
|
68
51
|
};
|
|
@@ -6,69 +6,39 @@ 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
|
-
export const barOrganizationDropdownOpen = writable(false);
|
|
10
|
-
export const barOrganizations = writable([]);
|
|
11
9
|
export function setInstanceAndProduct(instance_, product_) {
|
|
12
10
|
instance = instance_;
|
|
13
11
|
product = product_;
|
|
14
12
|
}
|
|
15
|
-
|
|
16
|
-
* @throws Error if initialization fails
|
|
17
|
-
*/
|
|
18
|
-
export async function initBar() {
|
|
13
|
+
export function initBar() {
|
|
19
14
|
const query = new URLSearchParams();
|
|
20
15
|
query.set('product', product);
|
|
21
16
|
const lastUnreadTime = UnreadUpdatesTimeLocalStorage.get();
|
|
22
17
|
if (lastUnreadTime) {
|
|
23
18
|
query.set('last_read_updates_at', lastUnreadTime.toString());
|
|
24
19
|
}
|
|
25
|
-
|
|
20
|
+
fetch(instance + '/api/public/bar?' + query.toString(), {
|
|
26
21
|
credentials: 'include'
|
|
22
|
+
})
|
|
23
|
+
.then((response) => response.json())
|
|
24
|
+
.then((data) => {
|
|
25
|
+
barUser.set(data.user);
|
|
26
|
+
barUnreadUpdates.set(data.updates.unread);
|
|
27
|
+
barLicense.set(data.billing.license);
|
|
28
|
+
barHasFailedInvoices.set(data.billing.has_failed_invoices);
|
|
29
|
+
if (lastUnreadTime === null) {
|
|
30
|
+
UnreadUpdatesTimeLocalStorage.setNow();
|
|
31
|
+
}
|
|
32
|
+
if (data.user && track.ready()) {
|
|
33
|
+
track.identify(data.user.id.toString(), {
|
|
34
|
+
name: data.user.name ?? undefined,
|
|
35
|
+
avatar: data.user.picture_url ?? undefined
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
.catch((error) => {
|
|
40
|
+
console.error('Error:', error);
|
|
27
41
|
});
|
|
28
|
-
if (!response.ok) {
|
|
29
|
-
throw new Error('Failed to initialize bar');
|
|
30
|
-
}
|
|
31
|
-
const data = await response.json();
|
|
32
|
-
barUser.set(data.user);
|
|
33
|
-
barUnreadUpdates.set(data.updates.unread);
|
|
34
|
-
barLicense.set(data.billing.license);
|
|
35
|
-
barHasFailedInvoices.set(data.billing.has_failed_invoices);
|
|
36
|
-
if (lastUnreadTime === null) {
|
|
37
|
-
UnreadUpdatesTimeLocalStorage.setNow();
|
|
38
|
-
}
|
|
39
|
-
if (data.user && track.ready()) {
|
|
40
|
-
track.identify(data.user.id.toString(), {
|
|
41
|
-
name: data.user.name ?? undefined,
|
|
42
|
-
avatar: data.user.picture_url ?? undefined,
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
export async function getMyOrganizations() {
|
|
47
|
-
/* return [
|
|
48
|
-
{ id: 1, name: 'Org 1', role: 'admin' },
|
|
49
|
-
{ id: 2, name: 'Org 2', role: 'member' },
|
|
50
|
-
{ 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', {
|
|
54
|
-
credentials: 'include'
|
|
55
|
-
});
|
|
56
|
-
const data = await response.json();
|
|
57
|
-
return data.organizations;
|
|
58
|
-
}
|
|
59
|
-
export async function switchOrganization(org) {
|
|
60
|
-
const response = await fetch(instance + '/api/public/bar/switch-org', {
|
|
61
|
-
method: 'POST',
|
|
62
|
-
credentials: 'include',
|
|
63
|
-
headers: {
|
|
64
|
-
'Content-Type': 'application/json'
|
|
65
|
-
},
|
|
66
|
-
body: JSON.stringify({ organization_id: org.id })
|
|
67
|
-
});
|
|
68
|
-
if (!response.ok) {
|
|
69
|
-
throw new Error('Failed to switch organization');
|
|
70
|
-
}
|
|
71
|
-
await initBar();
|
|
72
42
|
}
|
|
73
43
|
export class UnreadUpdatesTimeLocalStorage {
|
|
74
44
|
static KEY = 'unread_updates';
|
|
@@ -128,9 +98,5 @@ export const bar = {
|
|
|
128
98
|
*/
|
|
129
99
|
reload: () => {
|
|
130
100
|
initBar();
|
|
131
|
-
}
|
|
132
|
-
/**
|
|
133
|
-
* Open the org selector dropdown from the outside world
|
|
134
|
-
*/
|
|
135
|
-
openOrganizationDropdown: () => barOrganizationDropdownOpen.set(true)
|
|
101
|
+
}
|
|
136
102
|
};
|
|
@@ -4,35 +4,46 @@
|
|
|
4
4
|
import IconButton from '../../components/IconButton/IconButton.svelte';
|
|
5
5
|
import Dropdown from '../../components/Dropdown/Dropdown.svelte';
|
|
6
6
|
import IconList from '@hyvor/icons/IconList';
|
|
7
|
+
import HeaderNotification from './HeaderNotification.svelte';
|
|
7
8
|
|
|
8
9
|
interface Props {
|
|
9
|
-
|
|
10
|
+
product: string;
|
|
11
|
+
instance?: string;
|
|
12
|
+
logo?: string;
|
|
10
13
|
name?: string;
|
|
11
14
|
href?: string;
|
|
12
15
|
subName?: undefined | string;
|
|
13
16
|
darkToggle?: boolean;
|
|
14
17
|
center?: import('svelte').Snippet;
|
|
15
18
|
end?: import('svelte').Snippet;
|
|
16
|
-
|
|
19
|
+
max?: boolean;
|
|
17
20
|
}
|
|
18
21
|
|
|
19
22
|
let {
|
|
23
|
+
product,
|
|
20
24
|
logo,
|
|
25
|
+
instance = 'https://hyvor.com',
|
|
21
26
|
name = 'HYVOR',
|
|
22
27
|
href = '/',
|
|
23
28
|
subName = undefined,
|
|
24
29
|
darkToggle = true,
|
|
25
30
|
center,
|
|
26
31
|
end,
|
|
27
|
-
|
|
32
|
+
max = false
|
|
28
33
|
}: Props = $props();
|
|
29
34
|
</script>
|
|
30
35
|
|
|
31
36
|
<header>
|
|
37
|
+
<HeaderNotification {instance} {product} />
|
|
32
38
|
<Container as="nav" {max}>
|
|
33
39
|
<div class="nav-start">
|
|
34
40
|
<a class="nav-brand" {href}>
|
|
35
|
-
<img
|
|
41
|
+
<img
|
|
42
|
+
src={logo || `${instance}/api/public/logo/${product}.svg`}
|
|
43
|
+
alt="{name + (subName ? ' ' + subName : '')} Logo"
|
|
44
|
+
width="30"
|
|
45
|
+
height="30"
|
|
46
|
+
/>
|
|
36
47
|
<span class="brand-product">
|
|
37
48
|
<span class="brand">{name}</span>
|
|
38
49
|
{#if subName}
|
|
@@ -56,25 +67,6 @@
|
|
|
56
67
|
<DarkToggle />
|
|
57
68
|
</span>
|
|
58
69
|
{/if}
|
|
59
|
-
|
|
60
|
-
<!-- <span class="mobile-nav-wrap">
|
|
61
|
-
<Dropdown align="end" width={300}>
|
|
62
|
-
<IconButton
|
|
63
|
-
color="invisible"
|
|
64
|
-
slot="trigger"
|
|
65
|
-
>
|
|
66
|
-
<IconList size={18} />
|
|
67
|
-
</IconButton>
|
|
68
|
-
<div slot="content" class="mobile-content">
|
|
69
|
-
<div class="mobile-inner center">
|
|
70
|
-
<slot name="center" />
|
|
71
|
-
</div>
|
|
72
|
-
<div class="mobile-inner end">
|
|
73
|
-
<slot name="end" />
|
|
74
|
-
</div>
|
|
75
|
-
</div>
|
|
76
|
-
</Dropdown>
|
|
77
|
-
</span> -->
|
|
78
70
|
</div>
|
|
79
71
|
|
|
80
72
|
<span class="mobile-nav-wrap">
|
|
@@ -101,120 +93,119 @@
|
|
|
101
93
|
|
|
102
94
|
<div class="header-space"></div>
|
|
103
95
|
|
|
104
|
-
<style
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
.nav-brand img {
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
.brand-product {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
.mobile-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
}</style>
|
|
96
|
+
<style>
|
|
97
|
+
.header-space {
|
|
98
|
+
height: var(--header-height);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
header {
|
|
102
|
+
position: fixed;
|
|
103
|
+
top: 0;
|
|
104
|
+
left: 0;
|
|
105
|
+
width: 100%;
|
|
106
|
+
z-index: 100;
|
|
107
|
+
background-color: var(--background, var(--accent-lightest));
|
|
108
|
+
border-bottom: 1px solid var(--border);
|
|
109
|
+
height: var(--header-height);
|
|
110
|
+
display: flex;
|
|
111
|
+
flex-direction: column;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
header :global(> nav) {
|
|
115
|
+
display: flex;
|
|
116
|
+
align-items: center;
|
|
117
|
+
flex: 1;
|
|
118
|
+
}
|
|
119
|
+
.nav-brand {
|
|
120
|
+
display: inline-block;
|
|
121
|
+
line-height: inherit;
|
|
122
|
+
white-space: nowrap;
|
|
123
|
+
color: inherit;
|
|
124
|
+
text-decoration: none;
|
|
125
|
+
font-weight: 600;
|
|
126
|
+
}
|
|
127
|
+
.nav-brand img {
|
|
128
|
+
vertical-align: middle;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.brand-product {
|
|
132
|
+
vertical-align: middle;
|
|
133
|
+
display: inline-flex;
|
|
134
|
+
flex-direction: column;
|
|
135
|
+
justify-content: center;
|
|
136
|
+
line-height: 14px;
|
|
137
|
+
}
|
|
138
|
+
.brand-product .brand {
|
|
139
|
+
font-size: 14px;
|
|
140
|
+
}
|
|
141
|
+
.brand-product .product {
|
|
142
|
+
font-size: 12px;
|
|
143
|
+
font-weight: normal;
|
|
144
|
+
color: var(--text-light);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.nav-center {
|
|
148
|
+
flex: 1;
|
|
149
|
+
display: flex;
|
|
150
|
+
align-items: center;
|
|
151
|
+
gap: 6px;
|
|
152
|
+
justify-content: center;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.nav-end {
|
|
156
|
+
display: flex;
|
|
157
|
+
align-items: center;
|
|
158
|
+
gap: 6px;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.mobile-nav-wrap {
|
|
162
|
+
display: none;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.dark-mobile {
|
|
166
|
+
display: inline-flex;
|
|
167
|
+
align-items: center;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.dark-toggle-wrap {
|
|
171
|
+
margin-left: 8px;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
@media screen and (max-width: 992px) {
|
|
175
|
+
.nav-center {
|
|
176
|
+
display: none;
|
|
177
|
+
}
|
|
178
|
+
.nav-end {
|
|
179
|
+
display: none;
|
|
180
|
+
}
|
|
181
|
+
.mobile-nav-wrap {
|
|
182
|
+
display: inline-block;
|
|
183
|
+
}
|
|
184
|
+
.dark-mobile {
|
|
185
|
+
flex: 1;
|
|
186
|
+
text-align: right;
|
|
187
|
+
display: inline-block;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.mobile-content,
|
|
192
|
+
.mobile-inner {
|
|
193
|
+
display: flex;
|
|
194
|
+
flex-direction: column;
|
|
195
|
+
}
|
|
196
|
+
.mobile-content {
|
|
197
|
+
gap: 10px;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.mobile-content :global(.button) {
|
|
201
|
+
display: flex;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/*
|
|
205
|
+
Scroll padding top is used to prevent the content from being hidden behind the header
|
|
206
|
+
https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-padding-top
|
|
207
|
+
*/
|
|
208
|
+
:global(html) {
|
|
209
|
+
scroll-padding-top: calc(var(--header-height) + 20px);
|
|
210
|
+
}
|
|
211
|
+
</style>
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { BarUpdate } from '../../components/HyvorBar/bar.js';
|
|
3
|
+
import { onMount } from 'svelte';
|
|
4
|
+
import { slide } from 'svelte/transition';
|
|
5
|
+
import { isCloud } from '../cloud.js';
|
|
6
|
+
import IconMegaphone from '@hyvor/icons/IconMegaphone';
|
|
7
|
+
|
|
8
|
+
let notificationUpdate: BarUpdate | null = $state(null);
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
instance: string;
|
|
12
|
+
product: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let { instance, product }: Props = $props();
|
|
16
|
+
|
|
17
|
+
function set(u: BarUpdate) {
|
|
18
|
+
notificationUpdate = u;
|
|
19
|
+
document.documentElement.style.setProperty('--header-height', '85px');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
onMount(() => {
|
|
23
|
+
if (!isCloud(false)) return;
|
|
24
|
+
|
|
25
|
+
fetch(instance + '/api/public/updates/notification?type=' + product)
|
|
26
|
+
.then((response) => response.json())
|
|
27
|
+
.then((data) => {
|
|
28
|
+
if (data.update) {
|
|
29
|
+
set(data.update);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
{#if notificationUpdate}
|
|
36
|
+
<a
|
|
37
|
+
class="header-notification"
|
|
38
|
+
href={notificationUpdate.url}
|
|
39
|
+
target="_blank"
|
|
40
|
+
transition:slide={{ duration: 300 }}
|
|
41
|
+
>
|
|
42
|
+
<IconMegaphone size={12} />
|
|
43
|
+
{notificationUpdate.title} →
|
|
44
|
+
</a>
|
|
45
|
+
{/if}
|
|
46
|
+
|
|
47
|
+
<style>
|
|
48
|
+
.header-notification {
|
|
49
|
+
display: block;
|
|
50
|
+
background-color: var(--accent);
|
|
51
|
+
color: var(--accent-text);
|
|
52
|
+
height: 30px;
|
|
53
|
+
display: flex;
|
|
54
|
+
align-items: center;
|
|
55
|
+
justify-content: center;
|
|
56
|
+
font-size: 14px;
|
|
57
|
+
cursor: pointer;
|
|
58
|
+
font-weight: 600;
|
|
59
|
+
transition: opacity 0.2s;
|
|
60
|
+
}
|
|
61
|
+
.header-notification:hover {
|
|
62
|
+
opacity: 0.8;
|
|
63
|
+
}
|
|
64
|
+
</style>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isCloud(forceProd?: boolean): boolean;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function isCloud(forceProd = true) {
|
|
2
|
+
if (typeof window === 'undefined') {
|
|
3
|
+
return false;
|
|
4
|
+
}
|
|
5
|
+
const hostname = window.location.hostname;
|
|
6
|
+
function isDomain(domain) {
|
|
7
|
+
return hostname === domain || hostname.endsWith(`.${domain}`);
|
|
8
|
+
}
|
|
9
|
+
let domains = ['hyvor.com'];
|
|
10
|
+
if (forceProd === false) {
|
|
11
|
+
domains.push('hyvor.localhost');
|
|
12
|
+
domains.push('hyvorstaging.com');
|
|
13
|
+
}
|
|
14
|
+
return domains.some(isDomain);
|
|
15
|
+
}
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import { OpenPanel } from '@openpanel/web';
|
|
2
|
+
import { isCloud } from '../cloud.js';
|
|
2
3
|
class Track {
|
|
3
4
|
op;
|
|
4
|
-
isProductionDomain() {
|
|
5
|
-
const hostname = window.location.hostname;
|
|
6
|
-
return hostname === 'hyvor.com' || hostname.endsWith('.hyvor.com');
|
|
7
|
-
}
|
|
8
5
|
init({ forceTrack = false, openPanelApiUrl = 'https://op.hyvor.com/api', openPanelClientId = 'b11f6143-a6b0-4fa4-a86c-3969c01dbb1d', context = {} } = {}) {
|
|
9
|
-
if (!forceTrack && !
|
|
6
|
+
if (!forceTrack && !isCloud()) {
|
|
10
7
|
console.log('Tracking is disabled in non-production domains.');
|
|
11
8
|
return;
|
|
12
9
|
}
|
|
@@ -15,7 +12,7 @@ class Track {
|
|
|
15
12
|
clientId: openPanelClientId,
|
|
16
13
|
trackScreenViews: true,
|
|
17
14
|
trackOutgoingLinks: true,
|
|
18
|
-
trackAttributes: true
|
|
15
|
+
trackAttributes: true
|
|
19
16
|
});
|
|
20
17
|
if (Object.keys(context).length > 0) {
|
|
21
18
|
this.op.setGlobalProperties(context);
|
package/package.json
CHANGED
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import Dropdown from '../../Dropdown/Dropdown.svelte';
|
|
3
|
-
import Tooltip from '../../Tooltip/Tooltip.svelte';
|
|
4
|
-
import {
|
|
5
|
-
barOrganizationDropdownOpen,
|
|
6
|
-
barUser,
|
|
7
|
-
switchOrganization,
|
|
8
|
-
type BarOrganization
|
|
9
|
-
} from '../bar.js';
|
|
10
|
-
import IconChevronExpand from '@hyvor/icons/IconChevronExpand';
|
|
11
|
-
import OrgsList from './OrgsList.svelte';
|
|
12
|
-
import toast from '../../Toast/toast.js';
|
|
13
|
-
|
|
14
|
-
let disableTooltip = $state(true);
|
|
15
|
-
let switching = $state(false);
|
|
16
|
-
|
|
17
|
-
let props: {
|
|
18
|
-
onSwitch: (org: BarOrganization) => void;
|
|
19
|
-
} = $props();
|
|
20
|
-
|
|
21
|
-
async function handleSwitch(org: BarOrganization) {
|
|
22
|
-
barOrganizationDropdownOpen.set(false);
|
|
23
|
-
switching = true;
|
|
24
|
-
|
|
25
|
-
try {
|
|
26
|
-
await switchOrganization(org);
|
|
27
|
-
} catch (e) {
|
|
28
|
-
toast.error('Failed to switch organization.');
|
|
29
|
-
switching = false;
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
switching = false;
|
|
34
|
-
props.onSwitch(org);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
barOrganizationDropdownOpen.subscribe((value) => {
|
|
38
|
-
disableTooltip = true;
|
|
39
|
-
});
|
|
40
|
-
</script>
|
|
41
|
-
|
|
42
|
-
{#if $barUser}
|
|
43
|
-
<Tooltip position="bottom" text="Switch Organization" disabled={disableTooltip}>
|
|
44
|
-
<span class="wrap">
|
|
45
|
-
<Dropdown
|
|
46
|
-
bind:show={$barOrganizationDropdownOpen}
|
|
47
|
-
align="center"
|
|
48
|
-
width={275}
|
|
49
|
-
contentPadding={0}
|
|
50
|
-
>
|
|
51
|
-
{#snippet trigger()}
|
|
52
|
-
<button>
|
|
53
|
-
{#if switching}
|
|
54
|
-
<span class="switching">Switching...</span>
|
|
55
|
-
{:else}
|
|
56
|
-
{$barUser.current_organization_name}
|
|
57
|
-
{/if}
|
|
58
|
-
<IconChevronExpand size={12} />
|
|
59
|
-
</button>
|
|
60
|
-
{/snippet}
|
|
61
|
-
{#snippet content()}
|
|
62
|
-
<OrgsList onSwitch={handleSwitch} />
|
|
63
|
-
{/snippet}
|
|
64
|
-
</Dropdown>
|
|
65
|
-
</span>
|
|
66
|
-
</Tooltip>
|
|
67
|
-
{/if}
|
|
68
|
-
|
|
69
|
-
<style>
|
|
70
|
-
.wrap {
|
|
71
|
-
height: 100%;
|
|
72
|
-
margin-left: 20px;
|
|
73
|
-
margin-right: 20px;
|
|
74
|
-
display: inline-flex;
|
|
75
|
-
align-items: center;
|
|
76
|
-
}
|
|
77
|
-
button {
|
|
78
|
-
height: 100%;
|
|
79
|
-
padding: 0 15px;
|
|
80
|
-
font-size: 14px;
|
|
81
|
-
height: 24px;
|
|
82
|
-
font-weight: 600;
|
|
83
|
-
transition: 0.2s background-color;
|
|
84
|
-
border: 1px solid var(--border);
|
|
85
|
-
border-top: none;
|
|
86
|
-
border-bottom: none;
|
|
87
|
-
}
|
|
88
|
-
button:hover {
|
|
89
|
-
background-color: var(--hover);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
.wrap :global(.dropdown),
|
|
93
|
-
.wrap :global(.trigger) {
|
|
94
|
-
display: inline-block;
|
|
95
|
-
height: 100%;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
.switching {
|
|
99
|
-
opacity: 0.4;
|
|
100
|
-
animation: switching-pulse 1.5s infinite;
|
|
101
|
-
}
|
|
102
|
-
@keyframes switching-pulse {
|
|
103
|
-
0%,
|
|
104
|
-
100% {
|
|
105
|
-
opacity: 0.4;
|
|
106
|
-
}
|
|
107
|
-
50% {
|
|
108
|
-
opacity: 0.8;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
</style>
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { type BarOrganization } from '../bar.js';
|
|
2
|
-
type $$ComponentProps = {
|
|
3
|
-
onSwitch: (org: BarOrganization) => void;
|
|
4
|
-
};
|
|
5
|
-
declare const BarOrganization: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
6
|
-
type BarOrganization = ReturnType<typeof BarOrganization>;
|
|
7
|
-
export default BarOrganization;
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import IconMessage from '../../IconMessage/IconMessage.svelte';
|
|
3
|
-
import Loader from '../../Loader/Loader.svelte';
|
|
4
|
-
import { onMount } from 'svelte';
|
|
5
|
-
import { barOrganizations, getMyOrganizations, type BarOrganization } from '../bar.js';
|
|
6
|
-
import ActionList from '../../ActionList/ActionList.svelte';
|
|
7
|
-
import ActionListItem from '../../ActionList/ActionListItem.svelte';
|
|
8
|
-
|
|
9
|
-
let loading = $state(true);
|
|
10
|
-
let error = $state('');
|
|
11
|
-
|
|
12
|
-
let {
|
|
13
|
-
onSwitch
|
|
14
|
-
}: {
|
|
15
|
-
onSwitch: (org: BarOrganization) => void;
|
|
16
|
-
} = $props();
|
|
17
|
-
|
|
18
|
-
onMount(() => {
|
|
19
|
-
if ($barOrganizations.length > 0) {
|
|
20
|
-
loading = false;
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
getMyOrganizations()
|
|
24
|
-
.then((data) => {
|
|
25
|
-
barOrganizations.set(data);
|
|
26
|
-
})
|
|
27
|
-
.catch((err) => {
|
|
28
|
-
error = 'Failed to load organizations.';
|
|
29
|
-
})
|
|
30
|
-
.finally(() => {
|
|
31
|
-
loading = false;
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
</script>
|
|
35
|
-
|
|
36
|
-
{#if loading}
|
|
37
|
-
<Loader padding={40} block size="small" />
|
|
38
|
-
{:else if error}
|
|
39
|
-
<IconMessage error iconSize={30}>
|
|
40
|
-
{error}
|
|
41
|
-
</IconMessage>
|
|
42
|
-
{:else}
|
|
43
|
-
<div class="list-wrap">
|
|
44
|
-
<ActionList>
|
|
45
|
-
{#each $barOrganizations as org}
|
|
46
|
-
<ActionListItem on:select={() => onSwitch(org)}>
|
|
47
|
-
{org.name}
|
|
48
|
-
{#snippet end()}
|
|
49
|
-
<span class="role">
|
|
50
|
-
{org.role.toUpperCase()}
|
|
51
|
-
</span>
|
|
52
|
-
{/snippet}
|
|
53
|
-
</ActionListItem>
|
|
54
|
-
{/each}
|
|
55
|
-
</ActionList>
|
|
56
|
-
</div>
|
|
57
|
-
{/if}
|
|
58
|
-
|
|
59
|
-
<style>
|
|
60
|
-
.role {
|
|
61
|
-
font-size: 10px;
|
|
62
|
-
color: var(--text-light);
|
|
63
|
-
font-weight: 600;
|
|
64
|
-
}
|
|
65
|
-
.list-wrap {
|
|
66
|
-
max-height: 300px;
|
|
67
|
-
overflow-y: auto;
|
|
68
|
-
padding: 10px;
|
|
69
|
-
}
|
|
70
|
-
</style>
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { type BarOrganization } from '../bar.js';
|
|
2
|
-
type $$ComponentProps = {
|
|
3
|
-
onSwitch: (org: BarOrganization) => void;
|
|
4
|
-
};
|
|
5
|
-
declare const OrgsList: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
6
|
-
type OrgsList = ReturnType<typeof OrgsList>;
|
|
7
|
-
export default OrgsList;
|