@wazobiatech/wazui 0.0.10 → 0.0.12
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/.storybook/main.ts +41 -0
- package/.storybook/preview-head.html +3 -0
- package/.storybook/preview.ts +53 -0
- package/package.json +9 -3
- package/src/assets/dashboard/book.svg +6 -0
- package/src/assets/dashboard/edit.svg +5 -0
- package/src/assets/dashboard/emerald.svg +9 -0
- package/src/assets/dashboard/eye.svg +4 -0
- package/src/assets/dashboard/gold.svg +9 -0
- package/src/assets/dashboard/percentage-square.svg +6 -0
- package/src/assets/dashboard/sapphire.svg +9 -0
- package/src/assets/dashboard/send-2.svg +4 -0
- package/src/assets/desktop.svg +7 -0
- package/src/assets/fonts/UniversalFeature/FeatureJungle-Light.otf +0 -0
- package/src/assets/fonts/UniversalFeature/FeatureJungle-Light.woff +0 -0
- package/src/assets/fonts/UniversalFeature/FeatureJungle-Light.woff2 +0 -0
- package/src/assets/fonts/UniversalFeature/FeatureJungle-Regular.otf +0 -0
- package/src/assets/fonts/UniversalFeature/FeatureJungle-Regular.woff +0 -0
- package/src/assets/fonts/UniversalFeature/FeatureJungle-Regular.woff2 +0 -0
- package/src/assets/fonts/UniversalJungle/UniversalJungle-420.otf +0 -0
- package/src/assets/fonts/UniversalJungle/UniversalJungle-420.woff +0 -0
- package/src/assets/fonts/UniversalJungle/UniversalJungle-420.woff2 +0 -0
- package/src/assets/fonts/UniversalJungle/UniversalJungle-515.otf +0 -0
- package/src/assets/fonts/UniversalJungle/UniversalJungle-515.woff +0 -0
- package/src/assets/fonts/UniversalJungle/UniversalJungle-515.woff2 +0 -0
- package/src/assets/instagram.svg +9 -0
- package/src/assets/mobile.svg +4 -0
- package/src/assets/no-data-image.svg +41 -0
- package/src/assets/no-image-icon.svg +84 -0
- package/src/assets/pexels.svg +9 -0
- package/src/assets/react.svg +1 -0
- package/src/assets/sounds/draft.mp3 +0 -0
- package/src/assets/sounds/error.mp3 +0 -0
- package/src/assets/sounds/publish.mp3 +0 -0
- package/src/assets/tablet.svg +4 -0
- package/src/assets/unsplash.svg +3 -0
- package/src/assets/upload-image.svg +43 -0
- package/src/assets/user.svg +4 -0
- package/src/components/shadcn/index.ts +31 -0
- package/src/config/env.ts +15 -0
- package/src/contexts/ThemeContext.tsx +11 -0
- package/src/data/users.json +6014 -0
- package/src/decorators/ActionModalDecorator.tsx +10 -0
- package/src/decorators/ApolloDecorator.tsx +19 -0
- package/src/decorators/DolosApolloDecorator.tsx +31 -0
- package/src/decorators/JestDecorators.tsx +34 -0
- package/src/decorators/ReactQueryDecorator.tsx +13 -0
- package/src/decorators/RouterDecorator.tsx +5 -0
- package/src/decorators/UnsafePropsDecorator.tsx +0 -0
- package/src/env.ts +99 -0
- package/src/hooks/use-autogrow.ts +28 -0
- package/src/hooks/use-mobile.ts +19 -0
- package/src/hooks/useAuthors.ts +78 -0
- package/src/hooks/useDebounce.ts +18 -0
- package/src/hooks/useMercuryProjectToken.ts +112 -0
- package/src/hooks/useProjectToken.ts +21 -0
- package/src/hooks/useTheme.ts +12 -0
- package/src/index.css +397 -0
- package/src/index.ts +104 -0
- package/src/mocks/env.ts +12 -0
- package/src/providers/QueryProvider/index.tsx +10 -0
- package/src/providers/ThemeProvider/index.tsx +52 -0
- package/src/types/category.types.ts +13 -0
- package/src/types/image.types.ts +44 -0
- package/src/types/post.types.ts +121 -0
- package/src/types/theme.types.ts +10 -0
- package/src/types/users.types.ts +5 -0
- package/src/utils/apolloClient.ts +160 -0
- package/src/utils/articleMetadata.ts +97 -0
- package/src/utils/audio.ts +10 -0
- package/src/utils/imageProcessing.ts +399 -0
- package/src/utils/images.ts +279 -0
- package/src/utils/mercury.ts +0 -0
- package/src/utils/utils.ts +638 -0
- package/src/utils/videos.ts +29 -0
- package/src/vite-env.d.ts +1 -0
- package/.env.test +0 -5
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
export interface Category {
|
|
2
|
+
id?: string;
|
|
3
|
+
name: string;
|
|
4
|
+
description?: string | null;
|
|
5
|
+
created_at?: string;
|
|
6
|
+
updated_at?: string;
|
|
7
|
+
uuid: string;
|
|
8
|
+
slug?: string;
|
|
9
|
+
subCategories?: Category[];
|
|
10
|
+
parent?: Category;
|
|
11
|
+
}
|
|
12
|
+
export interface Tag {
|
|
13
|
+
id?: string;
|
|
14
|
+
name: string;
|
|
15
|
+
description?: string | null;
|
|
16
|
+
created_at?: string;
|
|
17
|
+
updated_at?: string;
|
|
18
|
+
uuid: string;
|
|
19
|
+
slug?: string;
|
|
20
|
+
subCategories?: Category[];
|
|
21
|
+
parent?: Category;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface ImageCoordinates {
|
|
25
|
+
left: number | null;
|
|
26
|
+
top: number | null;
|
|
27
|
+
right: number | null;
|
|
28
|
+
bottom: number | null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface ScreenCoordinates {
|
|
32
|
+
desktop: ImageCoordinates;
|
|
33
|
+
tablet: ImageCoordinates;
|
|
34
|
+
mobile: ImageCoordinates;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface FeaturedImage {
|
|
38
|
+
__typename: string;
|
|
39
|
+
title: string;
|
|
40
|
+
uuid: string;
|
|
41
|
+
url: string;
|
|
42
|
+
caption: string;
|
|
43
|
+
alternative_text: string;
|
|
44
|
+
metadata: string;
|
|
45
|
+
created_at: string;
|
|
46
|
+
updated_at: string;
|
|
47
|
+
src: string;
|
|
48
|
+
coordinates: ScreenCoordinates;
|
|
49
|
+
screenType: string;
|
|
50
|
+
width: number;
|
|
51
|
+
height: number;
|
|
52
|
+
original: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
interface PostConfig {
|
|
56
|
+
has_related_posts: boolean | null;
|
|
57
|
+
is_comments_enabled: boolean;
|
|
58
|
+
is_feature_article: boolean;
|
|
59
|
+
is_in_sitemap: boolean;
|
|
60
|
+
permalink: string;
|
|
61
|
+
related_posts_config: string;
|
|
62
|
+
schema: string;
|
|
63
|
+
social_preview_config: {
|
|
64
|
+
open_graph: object;
|
|
65
|
+
twitter_cards: object;
|
|
66
|
+
seo_cards: {
|
|
67
|
+
'seo:image': FeaturedImage;
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export type Post = {
|
|
73
|
+
id?: string;
|
|
74
|
+
uuid: string;
|
|
75
|
+
category_name?: string;
|
|
76
|
+
created_at?: string;
|
|
77
|
+
updated_at?: string;
|
|
78
|
+
excerpt: string;
|
|
79
|
+
author_uuid: string;
|
|
80
|
+
featured_image: string;
|
|
81
|
+
content: string;
|
|
82
|
+
categories: Category[];
|
|
83
|
+
tags: Tag[];
|
|
84
|
+
language_attr: string;
|
|
85
|
+
slug: string;
|
|
86
|
+
title: string;
|
|
87
|
+
metadata: string;
|
|
88
|
+
postConfig?: PostConfig;
|
|
89
|
+
status: number;
|
|
90
|
+
statistics: {
|
|
91
|
+
isLoading: boolean; shares: number; totalPageViews: number
|
|
92
|
+
};
|
|
93
|
+
author?: { uuid: string; name: string };
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// Article metadata type definitions
|
|
97
|
+
export interface OpenGraphData {
|
|
98
|
+
'og:title'?: string;
|
|
99
|
+
'og:image'?: string;
|
|
100
|
+
'og:description'?: string;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface TwitterCardsData {
|
|
104
|
+
'twitter:title'?: string;
|
|
105
|
+
'twitter:description'?: string;
|
|
106
|
+
'twitter:image'?: string;
|
|
107
|
+
}
|
|
108
|
+
export interface SEOCardsData {
|
|
109
|
+
'seo:title'?: string;
|
|
110
|
+
'seo:description'?: string;
|
|
111
|
+
'seo:image'?: string;
|
|
112
|
+
'seo:url'?: string;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface ArticleMetadata {
|
|
116
|
+
title?: string;
|
|
117
|
+
description?: string;
|
|
118
|
+
open_graph?: OpenGraphData;
|
|
119
|
+
seo_cards?: SEOCardsData;
|
|
120
|
+
twitter_cards?: TwitterCardsData;
|
|
121
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type Theme = 'dark' | 'light' | 'system' | 'brand';
|
|
2
|
+
export type ThemeProviderProps = {
|
|
3
|
+
children: React.ReactNode;
|
|
4
|
+
defaultTheme?: Theme;
|
|
5
|
+
storageKey?: string;
|
|
6
|
+
};
|
|
7
|
+
export type ThemeProviderState = {
|
|
8
|
+
theme: Theme;
|
|
9
|
+
setTheme: (theme: Theme) => void;
|
|
10
|
+
};
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { env } from '../env';
|
|
2
|
+
import { ApolloClient, InMemoryCache, FetchPolicy, createHttpLink } from '@apollo/client';
|
|
3
|
+
import { setContext } from '@apollo/client/link/context';
|
|
4
|
+
import UploadHttpLink from 'apollo-upload-client/UploadHttpLink.mjs';
|
|
5
|
+
import { fetchMercuryToken } from '../hooks/useMercuryProjectToken';
|
|
6
|
+
|
|
7
|
+
const createMuseAuthLink = (uri: string, isMuseClient: boolean = false) => {
|
|
8
|
+
return setContext(async (_operation, { headers: contextHeaders }) => {
|
|
9
|
+
const isMuseEndpoint = isMuseClient || uri.includes('muse');
|
|
10
|
+
|
|
11
|
+
if (isMuseEndpoint) {
|
|
12
|
+
try {
|
|
13
|
+
const response = await fetchMercuryToken();
|
|
14
|
+
const mercuryToken = response.token;
|
|
15
|
+
|
|
16
|
+
const newHeaders = {
|
|
17
|
+
...contextHeaders,
|
|
18
|
+
'X-Project-Token': `${mercuryToken}`,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
headers: newHeaders,
|
|
23
|
+
};
|
|
24
|
+
} catch (error) {
|
|
25
|
+
console.log('Error fetching Mercury token:', error);
|
|
26
|
+
return { headers: contextHeaders };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return { headers: contextHeaders };
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const createApolloClient = (
|
|
35
|
+
uri: string,
|
|
36
|
+
options?: {
|
|
37
|
+
headers?: Record<string, string>;
|
|
38
|
+
fetchPolicy?:
|
|
39
|
+
| 'cache-first'
|
|
40
|
+
| 'cache-and-network'
|
|
41
|
+
| 'network-only'
|
|
42
|
+
| 'cache-only'
|
|
43
|
+
| 'no-cache'
|
|
44
|
+
| 'standby';
|
|
45
|
+
errorPolicy?: 'none' | 'ignore' | 'all';
|
|
46
|
+
isMuseClient?: boolean;
|
|
47
|
+
}
|
|
48
|
+
) => {
|
|
49
|
+
const isMuseClient = options?.isMuseClient;
|
|
50
|
+
|
|
51
|
+
const defaultHeaders = {
|
|
52
|
+
// 'X-Internal-Auth': env.VITE_MUSE_TOKEN as string,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const headers = options?.headers ? { ...defaultHeaders, ...options.headers } : defaultHeaders;
|
|
56
|
+
const fetchPolicy = options?.fetchPolicy || 'no-cache';
|
|
57
|
+
const errorPolicy = options?.errorPolicy || 'all';
|
|
58
|
+
|
|
59
|
+
const httpLink = createHttpLink({
|
|
60
|
+
uri,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const authLink = setContext((_, { headers: contextHeaders }) => {
|
|
64
|
+
return {
|
|
65
|
+
headers: {
|
|
66
|
+
...contextHeaders,
|
|
67
|
+
...headers,
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const museAuthLink = createMuseAuthLink(uri, isMuseClient);
|
|
73
|
+
|
|
74
|
+
const client = new ApolloClient({
|
|
75
|
+
link: museAuthLink.concat(authLink).concat(httpLink),
|
|
76
|
+
cache: new InMemoryCache(),
|
|
77
|
+
defaultOptions: {
|
|
78
|
+
watchQuery: {
|
|
79
|
+
fetchPolicy: fetchPolicy as FetchPolicy,
|
|
80
|
+
errorPolicy,
|
|
81
|
+
},
|
|
82
|
+
query: {
|
|
83
|
+
fetchPolicy: fetchPolicy as FetchPolicy,
|
|
84
|
+
errorPolicy,
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
return client;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export const createApolloUploadClient = (
|
|
93
|
+
uri: string,
|
|
94
|
+
options?: {
|
|
95
|
+
headers?: Record<string, string>;
|
|
96
|
+
fetchPolicy?:
|
|
97
|
+
| 'cache-first'
|
|
98
|
+
| 'cache-and-network'
|
|
99
|
+
| 'network-only'
|
|
100
|
+
| 'cache-only'
|
|
101
|
+
| 'no-cache'
|
|
102
|
+
| 'standby';
|
|
103
|
+
errorPolicy?: 'none' | 'ignore' | 'all';
|
|
104
|
+
isMuseClient?: boolean;
|
|
105
|
+
}
|
|
106
|
+
) => {
|
|
107
|
+
const defaultHeaders = {
|
|
108
|
+
'X-Internal-Auth': env.VITE_MUSE_TOKEN as string,
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const headers = options?.headers ? { ...defaultHeaders, ...options.headers } : defaultHeaders;
|
|
112
|
+
const fetchPolicy = options?.fetchPolicy || 'no-cache';
|
|
113
|
+
const errorPolicy = options?.errorPolicy || 'all';
|
|
114
|
+
|
|
115
|
+
const uploadLink = new UploadHttpLink({
|
|
116
|
+
uri,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const authLink = setContext((_, { headers: contextHeaders }) => {
|
|
120
|
+
return {
|
|
121
|
+
headers: {
|
|
122
|
+
...contextHeaders,
|
|
123
|
+
...headers,
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const museAuthLink = createMuseAuthLink(uri, options?.isMuseClient);
|
|
129
|
+
|
|
130
|
+
const client = new ApolloClient({
|
|
131
|
+
link: museAuthLink.concat(authLink).concat(uploadLink),
|
|
132
|
+
cache: new InMemoryCache(),
|
|
133
|
+
defaultOptions: {
|
|
134
|
+
watchQuery: {
|
|
135
|
+
fetchPolicy: fetchPolicy as FetchPolicy,
|
|
136
|
+
errorPolicy,
|
|
137
|
+
},
|
|
138
|
+
query: {
|
|
139
|
+
fetchPolicy: fetchPolicy as FetchPolicy,
|
|
140
|
+
errorPolicy,
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
return client;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
export const blogClient = createApolloClient(env.VITE_GRAPH_QL_URL as string, {
|
|
149
|
+
isMuseClient: true,
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
export const blogClientNew = createApolloClient(env.VITE_MUSE_GRAPHQL_URL as string, {
|
|
153
|
+
isMuseClient: true,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
export const museClient = createApolloClient(env.VITE_MUSE_GRAPHQL_URL as string, {
|
|
157
|
+
isMuseClient: true,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
export const recipeClient = createApolloClient(env.VITE_RECIPE_GRAPHQL_URL as string);
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ArticleMetadata,
|
|
3
|
+
OpenGraphData,
|
|
4
|
+
SEOCardsData,
|
|
5
|
+
TwitterCardsData,
|
|
6
|
+
} from '../types/post.types';
|
|
7
|
+
|
|
8
|
+
export class ArticleMetadataConverter {
|
|
9
|
+
/**
|
|
10
|
+
* Converts a partial record to a properly typed ArticleMetadata object
|
|
11
|
+
* Validates and sanitizes data while preserving type safety
|
|
12
|
+
*/
|
|
13
|
+
static fromRecord(record: Record<string, string | Record<string, string>>): ArticleMetadata {
|
|
14
|
+
const metadata: ArticleMetadata = {};
|
|
15
|
+
|
|
16
|
+
// Extract basic fields
|
|
17
|
+
if (record.title && typeof record.title === 'string') {
|
|
18
|
+
metadata.title = record.title.trim();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (record.description && typeof record.description === 'string') {
|
|
22
|
+
metadata.description = record.description.trim();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Extract Open Graph data
|
|
26
|
+
if (record.open_graph && typeof record.open_graph === 'object') {
|
|
27
|
+
metadata.open_graph = this.extractOpenGraphData(record.open_graph);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Extract Twitter Cards data
|
|
31
|
+
if (record.twitter_cards && typeof record.twitter_cards === 'object') {
|
|
32
|
+
metadata.twitter_cards = this.extractTwitterCardsData(record.twitter_cards);
|
|
33
|
+
}
|
|
34
|
+
// Extract SEO Cards data
|
|
35
|
+
if (record.seo_cards && typeof record.seo_cards === 'object') {
|
|
36
|
+
metadata.seo_cards = this.extractSEOCardsData(record.seo_cards);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return metadata;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Extracts and validates Open Graph data
|
|
44
|
+
*/
|
|
45
|
+
private static extractOpenGraphData(ogData: Record<string, string>): OpenGraphData {
|
|
46
|
+
const openGraph: OpenGraphData = {};
|
|
47
|
+
|
|
48
|
+
// String fields
|
|
49
|
+
const stringFields = ['og:title', 'og:image', 'og:description'] as const;
|
|
50
|
+
|
|
51
|
+
for (const field of stringFields) {
|
|
52
|
+
if (ogData[field] && typeof ogData[field] === 'string') {
|
|
53
|
+
openGraph[field] = ogData[field].trim();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return openGraph;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Extracts and validates Twitter Cards data
|
|
61
|
+
*/
|
|
62
|
+
private static extractTwitterCardsData(twitterData: Record<string, string>): TwitterCardsData {
|
|
63
|
+
const twitterCards: TwitterCardsData = {};
|
|
64
|
+
|
|
65
|
+
const stringFields = ['twitter:title', 'twitter:description', 'twitter:image'] as const;
|
|
66
|
+
|
|
67
|
+
for (const field of stringFields) {
|
|
68
|
+
if (twitterData[field] && typeof twitterData[field] === 'string') {
|
|
69
|
+
twitterCards[field] = twitterData[field].trim();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return twitterCards;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Extracts and validates Twitter Cards data
|
|
77
|
+
*/
|
|
78
|
+
private static extractSEOCardsData(seoData: Record<string, string>): SEOCardsData {
|
|
79
|
+
const seoCards: SEOCardsData = {};
|
|
80
|
+
|
|
81
|
+
const stringFields = ['seo:title', 'seo:description', 'seo:image', 'seo:url'] as const;
|
|
82
|
+
|
|
83
|
+
for (const field of stringFields) {
|
|
84
|
+
if (seoData[field] && typeof seoData[field] === 'string') {
|
|
85
|
+
seoCards[field] = seoData[field].trim();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return seoCards;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Converts ArticleMetadata back to a plain object
|
|
93
|
+
*/
|
|
94
|
+
static toRecord(metadata: ArticleMetadata): Record<string, string | object> {
|
|
95
|
+
return JSON.parse(JSON.stringify(metadata));
|
|
96
|
+
}
|
|
97
|
+
}
|