@wazobiatech/wazui 0.0.9 → 0.0.11

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.
Files changed (77) hide show
  1. package/.storybook/main.ts +41 -0
  2. package/.storybook/preview-head.html +3 -0
  3. package/.storybook/preview.ts +53 -0
  4. package/package.json +1 -1
  5. package/src/assets/dashboard/book.svg +6 -0
  6. package/src/assets/dashboard/edit.svg +5 -0
  7. package/src/assets/dashboard/emerald.svg +9 -0
  8. package/src/assets/dashboard/eye.svg +4 -0
  9. package/src/assets/dashboard/gold.svg +9 -0
  10. package/src/assets/dashboard/percentage-square.svg +6 -0
  11. package/src/assets/dashboard/sapphire.svg +9 -0
  12. package/src/assets/dashboard/send-2.svg +4 -0
  13. package/src/assets/desktop.svg +7 -0
  14. package/src/assets/fonts/UniversalFeature/FeatureJungle-Light.otf +0 -0
  15. package/src/assets/fonts/UniversalFeature/FeatureJungle-Light.woff +0 -0
  16. package/src/assets/fonts/UniversalFeature/FeatureJungle-Light.woff2 +0 -0
  17. package/src/assets/fonts/UniversalFeature/FeatureJungle-Regular.otf +0 -0
  18. package/src/assets/fonts/UniversalFeature/FeatureJungle-Regular.woff +0 -0
  19. package/src/assets/fonts/UniversalFeature/FeatureJungle-Regular.woff2 +0 -0
  20. package/src/assets/fonts/UniversalJungle/UniversalJungle-420.otf +0 -0
  21. package/src/assets/fonts/UniversalJungle/UniversalJungle-420.woff +0 -0
  22. package/src/assets/fonts/UniversalJungle/UniversalJungle-420.woff2 +0 -0
  23. package/src/assets/fonts/UniversalJungle/UniversalJungle-515.otf +0 -0
  24. package/src/assets/fonts/UniversalJungle/UniversalJungle-515.woff +0 -0
  25. package/src/assets/fonts/UniversalJungle/UniversalJungle-515.woff2 +0 -0
  26. package/src/assets/instagram.svg +9 -0
  27. package/src/assets/mobile.svg +4 -0
  28. package/src/assets/no-data-image.svg +41 -0
  29. package/src/assets/no-image-icon.svg +84 -0
  30. package/src/assets/pexels.svg +9 -0
  31. package/src/assets/react.svg +1 -0
  32. package/src/assets/sounds/draft.mp3 +0 -0
  33. package/src/assets/sounds/error.mp3 +0 -0
  34. package/src/assets/sounds/publish.mp3 +0 -0
  35. package/src/assets/tablet.svg +4 -0
  36. package/src/assets/unsplash.svg +3 -0
  37. package/src/assets/upload-image.svg +43 -0
  38. package/src/assets/user.svg +4 -0
  39. package/src/components/shadcn/index.ts +31 -0
  40. package/src/config/env.ts +15 -0
  41. package/src/contexts/ThemeContext.tsx +11 -0
  42. package/src/data/users.json +6014 -0
  43. package/src/decorators/ActionModalDecorator.tsx +10 -0
  44. package/src/decorators/ApolloDecorator.tsx +19 -0
  45. package/src/decorators/DolosApolloDecorator.tsx +31 -0
  46. package/src/decorators/JestDecorators.tsx +34 -0
  47. package/src/decorators/ReactQueryDecorator.tsx +13 -0
  48. package/src/decorators/RouterDecorator.tsx +5 -0
  49. package/src/decorators/UnsafePropsDecorator.tsx +0 -0
  50. package/src/env.ts +99 -0
  51. package/src/hooks/use-autogrow.ts +28 -0
  52. package/src/hooks/use-mobile.ts +19 -0
  53. package/src/hooks/useAuthors.ts +78 -0
  54. package/src/hooks/useDebounce.ts +18 -0
  55. package/src/hooks/useMercuryProjectToken.ts +112 -0
  56. package/src/hooks/useProjectToken.ts +21 -0
  57. package/src/hooks/useTheme.ts +12 -0
  58. package/src/index.css +397 -0
  59. package/src/index.ts +104 -0
  60. package/src/mocks/env.ts +12 -0
  61. package/src/providers/QueryProvider/index.tsx +10 -0
  62. package/src/providers/ThemeProvider/index.tsx +52 -0
  63. package/src/types/category.types.ts +13 -0
  64. package/src/types/image.types.ts +44 -0
  65. package/src/types/post.types.ts +121 -0
  66. package/src/types/theme.types.ts +10 -0
  67. package/src/types/users.types.ts +5 -0
  68. package/src/utils/apolloClient.ts +160 -0
  69. package/src/utils/articleMetadata.ts +97 -0
  70. package/src/utils/audio.ts +10 -0
  71. package/src/utils/imageProcessing.ts +399 -0
  72. package/src/utils/images.ts +279 -0
  73. package/src/utils/mercury.ts +0 -0
  74. package/src/utils/utils.ts +638 -0
  75. package/src/utils/videos.ts +29 -0
  76. package/src/vite-env.d.ts +1 -0
  77. 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,5 @@
1
+ export type UserConfig = {
2
+ uuid: string;
3
+ name: string;
4
+ email: string;
5
+ };
@@ -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
+ }
@@ -0,0 +1,10 @@
1
+ export const playAudio = (file: string) => {
2
+ try {
3
+ new Audio(file).play().catch(error => {
4
+ console.log(error);
5
+ });
6
+ } catch (error) {
7
+ console.log(error);
8
+ /* ignore autoplay restrictions in tests */
9
+ }
10
+ };