@lobb-js/lobb-ext-auth 0.2.2 → 0.3.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.
Files changed (49) hide show
  1. package/dist/auth.d.ts +13 -0
  2. package/dist/auth.js +44 -0
  3. package/dist/index.d.ts +2 -0
  4. package/dist/index.js +52 -0
  5. package/dist/lib/components/pages/loginPage/index.svelte +64 -0
  6. package/dist/lib/components/pages/loginPage/index.svelte.d.ts +14 -0
  7. package/dist/lib/components/pages/settings/index.svelte +53 -0
  8. package/dist/lib/components/pages/settings/index.svelte.d.ts +14 -0
  9. package/dist/lib/components/pages/settings/pages/activityFeed.svelte +21 -0
  10. package/dist/lib/components/pages/settings/pages/activityFeed.svelte.d.ts +14 -0
  11. package/dist/lib/components/pages/settings/pages/rolesAndPermissions.svelte +21 -0
  12. package/dist/lib/components/pages/settings/pages/rolesAndPermissions.svelte.d.ts +14 -0
  13. package/dist/lib/components/pages/settings/pages/users.svelte +21 -0
  14. package/dist/lib/components/pages/settings/pages/users.svelte.d.ts +14 -0
  15. package/dist/lib/components/pages/userSettings/components/account.svelte +106 -0
  16. package/dist/lib/components/pages/userSettings/components/account.svelte.d.ts +14 -0
  17. package/dist/lib/components/pages/userSettings/components/profile.svelte +87 -0
  18. package/dist/lib/components/pages/userSettings/components/profile.svelte.d.ts +14 -0
  19. package/dist/lib/components/pages/userSettings/index.svelte +48 -0
  20. package/dist/lib/components/pages/userSettings/index.svelte.d.ts +14 -0
  21. package/dist/lib/index.d.ts +0 -0
  22. package/dist/lib/index.js +2 -0
  23. package/dist/lib/utils.d.ts +12 -0
  24. package/dist/lib/utils.js +5 -0
  25. package/dist/onStartup.d.ts +2 -0
  26. package/dist/onStartup.js +21 -0
  27. package/dist/tests/login.spec.d.ts +1 -0
  28. package/dist/tests/login.spec.js +27 -0
  29. package/dist/tests/package.json +1 -0
  30. package/dist/tests/playwright.config.cjs +27 -0
  31. package/dist/tests/playwright.config.d.cts +2 -0
  32. package/package.json +8 -4
  33. package/.vscode/settings.json +0 -5
  34. package/CHANGELOG.md +0 -149
  35. package/lobb.ts +0 -108
  36. package/public/public/vite.svg +0 -1
  37. package/public/vite.svg +0 -1
  38. package/scripts/postpublish.sh +0 -12
  39. package/scripts/prepublish.sh +0 -17
  40. package/studio/app.html +0 -12
  41. package/studio/routes/+layout.svelte +0 -7
  42. package/studio/routes/+layout.ts +0 -1
  43. package/studio/routes/[...path]/+page.svelte +0 -6
  44. package/svelte.config.js +0 -24
  45. package/todo.md +0 -37
  46. package/tsconfig.app.json +0 -27
  47. package/tsconfig.json +0 -13
  48. package/tsconfig.node.json +0 -26
  49. package/vite.config.ts +0 -8
package/dist/auth.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ import type { ExtensionUtils } from "@lobb-js/studio";
2
+ interface LoginPayload {
3
+ email: string;
4
+ password: string;
5
+ }
6
+ export declare class Auth {
7
+ private utils;
8
+ constructor(utils: ExtensionUtils);
9
+ getSession(): any;
10
+ login(payload: LoginPayload): Promise<void>;
11
+ logout(): Promise<void>;
12
+ }
13
+ export {};
package/dist/auth.js ADDED
@@ -0,0 +1,44 @@
1
+ export class Auth {
2
+ utils;
3
+ constructor(utils) {
4
+ this.utils = utils;
5
+ }
6
+ getSession() {
7
+ const lobbSession = localStorage.getItem("lobb_session");
8
+ if (lobbSession) {
9
+ const parsedLobbSession = JSON.parse(lobbSession);
10
+ this.utils.lobb.setHeaders({
11
+ Authorization: `Bearer ${parsedLobbSession.access_token.token}`,
12
+ });
13
+ return parsedLobbSession;
14
+ }
15
+ }
16
+ async login(payload) {
17
+ const response = await this.utils.lobb.request({
18
+ method: "POST",
19
+ route: "/api/collections/auth_sessions",
20
+ payload: {
21
+ data: payload,
22
+ },
23
+ });
24
+ if (response.status >= 400) {
25
+ return;
26
+ }
27
+ const result = await response.json();
28
+ const session = result.data;
29
+ localStorage.setItem("lobb_session", JSON.stringify(session));
30
+ this.utils.ctx.extensions.auth.session = session;
31
+ this.utils.lobb.setHeaders({
32
+ Authorization: `Bearer ${session.access_token.token}`,
33
+ });
34
+ this.utils.location.navigate("/studio");
35
+ }
36
+ async logout() {
37
+ await this.utils.lobb.request({
38
+ method: "DELETE",
39
+ route: "/api/extensions/auth/logout",
40
+ });
41
+ localStorage.removeItem("lobb_session");
42
+ this.utils.location.navigate("/studio/extensions/auth/login_page");
43
+ }
44
+ }
@@ -0,0 +1,2 @@
1
+ import type { Extension, ExtensionUtils } from "@lobb-js/studio";
2
+ export default function extension(utils: ExtensionUtils): Extension;
package/dist/index.js ADDED
@@ -0,0 +1,52 @@
1
+ import { Auth } from "./auth";
2
+ import { onStartup } from "./onStartup";
3
+ import LoginPage from "./lib/components/pages/loginPage/index.svelte";
4
+ import UserSettings from "./lib/components/pages/userSettings/index.svelte";
5
+ import Settings from "./lib/components/pages/settings/index.svelte";
6
+ // TODO we should export the extension object directly without a function at all. because we dont set configuration in here
7
+ export default function extension(utils) {
8
+ return {
9
+ name: "auth",
10
+ onStartup: onStartup,
11
+ components: {
12
+ "pages.login_page": LoginPage,
13
+ "pages.user_settings": UserSettings,
14
+ "pages.settings": Settings,
15
+ },
16
+ dashboardNavs: {
17
+ middle: [
18
+ {
19
+ label: "Auth",
20
+ icon: utils.components.Icons.Key,
21
+ href: "/studio/extensions/auth/settings/users",
22
+ },
23
+ ],
24
+ bottom: [
25
+ {
26
+ label: "My User",
27
+ icon: utils.components.Icons.User,
28
+ onclick: () => {
29
+ utils.location.navigate("/studio/extensions/auth/user_settings");
30
+ },
31
+ navs: [
32
+ {
33
+ label: "User Settings",
34
+ icon: utils.components.Icons.Settings,
35
+ onclick: () => {
36
+ utils.location.navigate("/studio/extensions/auth/user_settings");
37
+ },
38
+ },
39
+ {
40
+ label: "Logout",
41
+ icon: utils.components.Icons.LogOut,
42
+ onclick: () => {
43
+ const auth = new Auth(utils);
44
+ auth.logout();
45
+ },
46
+ },
47
+ ],
48
+ },
49
+ ],
50
+ },
51
+ };
52
+ }
@@ -0,0 +1,64 @@
1
+ <script lang="ts">
2
+ import type { ExtensionProps } from "@lobb-js/studio";
3
+ import { Auth } from "../../../../auth";
4
+
5
+ const { utils }: ExtensionProps = $props();
6
+
7
+ let formData = {
8
+ email:
9
+ window.location.hostname === "demo.lobb.app"
10
+ ? "admin@example.com"
11
+ : "",
12
+ password: window.location.hostname === "demo.lobb.app" ? "admin" : "",
13
+ };
14
+
15
+ async function handleOnSubmit(e: Event) {
16
+ e.preventDefault();
17
+ if (!formData.email && !formData.password) {
18
+ utils.toast.error("Please complete all fields");
19
+ return;
20
+ }
21
+
22
+ const auth = new Auth(utils);
23
+ await auth.login(formData);
24
+ }
25
+ </script>
26
+
27
+ <div
28
+ class="fixed left-0 top-0 flex h-screen w-screen items-center justify-center bg-muted"
29
+ >
30
+ <div class="flex w-full max-w-[25rem] flex-col gap-6 p-6">
31
+ <div>
32
+ <div class="text-4xl">Welcome back</div>
33
+ <div>Sign in to your account</div>
34
+ </div>
35
+ <form
36
+ class="flex flex-col gap-6 rounded-md border bg-white p-6"
37
+ onsubmit={handleOnSubmit}
38
+ >
39
+ <div class="flex flex-col gap-2">
40
+ <div>
41
+ <div class="mb-1 text-sm font-medium">Email</div>
42
+ <utils.components.Input
43
+ bind:value={formData.email}
44
+ autocomplete="email"
45
+ placeholder="email@example.com"
46
+ />
47
+ </div>
48
+ <div>
49
+ <div class="mb-1 text-sm font-medium">Password</div>
50
+ <utils.components.Input
51
+ bind:value={formData.password}
52
+ type="password"
53
+ autocomplete="current-password"
54
+ placeholder="••••••••"
55
+ />
56
+ </div>
57
+ </div>
58
+ <utils.components.Button type="submit">
59
+ Sign In
60
+ </utils.components.Button>
61
+ <div class="text-sm underline">Forgot Password?</div>
62
+ </form>
63
+ </div>
64
+ </div>
@@ -0,0 +1,14 @@
1
+ import { SvelteComponentTyped } from "svelte";
2
+ declare const __propDef: {
3
+ props: Record<string, never>;
4
+ events: {
5
+ [evt: string]: CustomEvent<any>;
6
+ };
7
+ slots: {};
8
+ };
9
+ export type IndexProps = typeof __propDef.props;
10
+ export type IndexEvents = typeof __propDef.events;
11
+ export type IndexSlots = typeof __propDef.slots;
12
+ export default class Index extends SvelteComponentTyped<IndexProps, IndexEvents, IndexSlots> {
13
+ }
14
+ export {};
@@ -0,0 +1,53 @@
1
+ <script lang="ts">
2
+ import type { ExtensionProps } from "@lobb-js/studio";
3
+ import Users from "./pages/users.svelte";
4
+ import RolesAndPermissions from "./pages/rolesAndPermissions.svelte";
5
+ import ActivityFeed from "./pages/activityFeed.svelte";
6
+
7
+ const props: ExtensionProps = $props();
8
+ const components = props.utils.components;
9
+ const { Sidebar } = components;
10
+ const { Icons } = components;
11
+ const auth_settings_page = $derived(props.utils.location.url.pathname.split("/")[5]);
12
+ const isSmall = $derived(!props.utils.mediaQueries.sm.current);
13
+ </script>
14
+
15
+ <!-- TODO: add the sidebar in here to add these pages
16
+ - users
17
+ - roles and permissions
18
+ - invitation page
19
+ - activity feed
20
+ -->
21
+
22
+ <Sidebar
23
+ title="Auth settings"
24
+ showSearch={false}
25
+ isSelected={(element: any) => {
26
+ const element_page = element.href?.split("/")[5];
27
+ return element_page === auth_settings_page;
28
+ }}
29
+ data={[
30
+ {
31
+ name: "Users",
32
+ href: "/studio/extensions/auth/settings/users",
33
+ icon: Icons.Users
34
+ },
35
+ {
36
+ name: "roles and permissions",
37
+ href: "/studio/extensions/auth/settings/roles_and_permissions",
38
+ icon: Icons.ShieldUser
39
+ },
40
+ {
41
+ name: "activity feed",
42
+ href: "/studio/extensions/auth/settings/activity_feed",
43
+ icon: Icons.ClipboardList
44
+ },
45
+ ]} >
46
+ {#if auth_settings_page === 'users'}
47
+ <Users {...props} />
48
+ {:else if auth_settings_page === 'roles_and_permissions'}
49
+ <RolesAndPermissions {...props} />
50
+ {:else if auth_settings_page === 'activity_feed'}
51
+ <ActivityFeed {...props} />
52
+ {/if}
53
+ </Sidebar>
@@ -0,0 +1,14 @@
1
+ import { SvelteComponentTyped } from "svelte";
2
+ declare const __propDef: {
3
+ props: Record<string, never>;
4
+ events: {
5
+ [evt: string]: CustomEvent<any>;
6
+ };
7
+ slots: {};
8
+ };
9
+ export type IndexProps = typeof __propDef.props;
10
+ export type IndexEvents = typeof __propDef.events;
11
+ export type IndexSlots = typeof __propDef.slots;
12
+ export default class Index extends SvelteComponentTyped<IndexProps, IndexEvents, IndexSlots> {
13
+ }
14
+ export {};
@@ -0,0 +1,21 @@
1
+ <script lang="ts">
2
+ import type { ExtensionProps } from "@lobb-js/studio";
3
+
4
+ const { utils }: ExtensionProps = $props();
5
+ const components = utils.components;
6
+ </script>
7
+
8
+ <div class="flex flex-col gap-4 p-4 h-full">
9
+ <div>
10
+ <div class="text-xl font-bold">Activity Feed</div>
11
+ <div class="text-sm text-muted-foreground">
12
+ Monitor your users Activity Feed.
13
+ </div>
14
+ </div>
15
+ <components.Separator />
16
+ <div class="flex gap-4 border rounded-md overflow-hidden h-full bg-muted/30">
17
+ <components.DataTable
18
+ collectionName="auth_activity_feed"
19
+ />
20
+ </div>
21
+ </div>
@@ -0,0 +1,14 @@
1
+ import { SvelteComponentTyped } from "svelte";
2
+ declare const __propDef: {
3
+ props: Record<string, never>;
4
+ events: {
5
+ [evt: string]: CustomEvent<any>;
6
+ };
7
+ slots: {};
8
+ };
9
+ export type ActivityFeedProps = typeof __propDef.props;
10
+ export type ActivityFeedEvents = typeof __propDef.events;
11
+ export type ActivityFeedSlots = typeof __propDef.slots;
12
+ export default class ActivityFeed extends SvelteComponentTyped<ActivityFeedProps, ActivityFeedEvents, ActivityFeedSlots> {
13
+ }
14
+ export {};
@@ -0,0 +1,21 @@
1
+ <script lang="ts">
2
+ import type { ExtensionProps } from "@lobb-js/studio";
3
+
4
+ const { utils }: ExtensionProps = $props();
5
+ const components = utils.components;
6
+ </script>
7
+
8
+ <div class="flex flex-col gap-4 p-4 h-full">
9
+ <div>
10
+ <div class="text-xl font-bold">Roles and Permissions</div>
11
+ <div class="text-sm text-muted-foreground">
12
+ Manage the Roles and Permissions of the system.
13
+ </div>
14
+ </div>
15
+ <components.Separator />
16
+ <div class="flex gap-4 border rounded-md overflow-hidden h-full bg-muted/30">
17
+ <components.DataTable
18
+ collectionName="auth_roles"
19
+ />
20
+ </div>
21
+ </div>
@@ -0,0 +1,14 @@
1
+ import { SvelteComponentTyped } from "svelte";
2
+ declare const __propDef: {
3
+ props: Record<string, never>;
4
+ events: {
5
+ [evt: string]: CustomEvent<any>;
6
+ };
7
+ slots: {};
8
+ };
9
+ export type RolesAndPermissionsProps = typeof __propDef.props;
10
+ export type RolesAndPermissionsEvents = typeof __propDef.events;
11
+ export type RolesAndPermissionsSlots = typeof __propDef.slots;
12
+ export default class RolesAndPermissions extends SvelteComponentTyped<RolesAndPermissionsProps, RolesAndPermissionsEvents, RolesAndPermissionsSlots> {
13
+ }
14
+ export {};
@@ -0,0 +1,21 @@
1
+ <script lang="ts">
2
+ import type { ExtensionProps } from "@lobb-js/studio";
3
+
4
+ const { utils }: ExtensionProps = $props();
5
+ const components = utils.components;
6
+ </script>
7
+
8
+ <div class="flex flex-col gap-4 p-4 h-full">
9
+ <div>
10
+ <div class="text-xl font-bold">Users</div>
11
+ <div class="text-sm text-muted-foreground">
12
+ Manage the users of the system.
13
+ </div>
14
+ </div>
15
+ <components.Separator />
16
+ <div class="flex gap-4 border rounded-md overflow-hidden h-full bg-muted/30">
17
+ <components.DataTable
18
+ collectionName="auth_users"
19
+ />
20
+ </div>
21
+ </div>
@@ -0,0 +1,14 @@
1
+ import { SvelteComponentTyped } from "svelte";
2
+ declare const __propDef: {
3
+ props: Record<string, never>;
4
+ events: {
5
+ [evt: string]: CustomEvent<any>;
6
+ };
7
+ slots: {};
8
+ };
9
+ export type UsersProps = typeof __propDef.props;
10
+ export type UsersEvents = typeof __propDef.events;
11
+ export type UsersSlots = typeof __propDef.slots;
12
+ export default class Users extends SvelteComponentTyped<UsersProps, UsersEvents, UsersSlots> {
13
+ }
14
+ export {};
@@ -0,0 +1,106 @@
1
+ <script lang="ts">
2
+ import type { ExtensionProps } from "@lobb-js/studio";
3
+ import { onMount } from "svelte";
4
+
5
+ const { utils }: ExtensionProps = $props();
6
+
7
+ let user = $state({
8
+ old_password: "",
9
+ new_password: "",
10
+ });
11
+ const icons = utils.components.Icons;
12
+
13
+ // @ts-ignore: Im sure the session object exist
14
+ const currentUserId = utils.ctx.extensions.auth.session.user.id;
15
+ let loaded = $state(false);
16
+ onMount(async () => {
17
+ const response = await utils.lobb.findOne("auth_users", currentUserId);
18
+ const result = await response.json();
19
+ user = result.data;
20
+ loaded = true;
21
+ });
22
+
23
+ async function onUpdateCLick() {
24
+ if (!user.new_password) {
25
+ utils.toast.error("Make sure you fill the new password");
26
+ return;
27
+ } else if (!user.old_password) {
28
+ utils.toast.error("Make sure you fill the old password");
29
+ return;
30
+ }
31
+
32
+ const result = await utils.lobb.request({
33
+ method: "POST",
34
+ route: "/api/extensions/auth/change_password",
35
+ payload: {
36
+ old_password: user.old_password,
37
+ new_password: user.new_password,
38
+ },
39
+ });
40
+
41
+ if (result.status >= 400) {
42
+ return;
43
+ }
44
+
45
+ utils.toast.success("Account updated successfully.");
46
+ }
47
+ </script>
48
+
49
+ {#if loaded}
50
+ <div class="flex max-w-[40rem] flex-1 flex-col gap-4 overflow-y-visible">
51
+ <div>
52
+ <div class="text-lg font-semibold">Account</div>
53
+ <div class="text-sm text-muted-foreground">
54
+ Update your account settings.
55
+ </div>
56
+ </div>
57
+ <utils.components.Separator />
58
+ <!-- TODO: add prefered language here too -->
59
+ <div class="flex flex-col gap-2">
60
+ <div class="text-sm">Change Password</div>
61
+ <div class="flex gap-4">
62
+ <utils.components.Input
63
+ bind:value={user.old_password}
64
+ placeholder="Old password"
65
+ type="password"
66
+ />
67
+ <utils.components.Input
68
+ bind:value={user.new_password}
69
+ placeholder="New password"
70
+ type="password"
71
+ />
72
+ </div>
73
+ <div class="text-xs text-muted-foreground">
74
+ This is where you can change your password.
75
+ </div>
76
+ </div>
77
+ <utils.components.Button
78
+ class="self-start"
79
+ onclick={onUpdateCLick}
80
+ Icon={icons.Pencil}
81
+ >
82
+ Update Account
83
+ </utils.components.Button>
84
+ </div>
85
+ {:else}
86
+ <div class="flex max-w-[40rem] flex-1 flex-col gap-8 overflow-y-visible">
87
+ <div class="grid gap-2">
88
+ <utils.components.Skeleton class="h-8 w-full max-w-60" />
89
+ <utils.components.Skeleton class="h-4 w-full max-w-96" />
90
+ </div>
91
+ <utils.components.Separator />
92
+ <div class="flex flex-col gap-2">
93
+ <utils.components.Skeleton class="h-4 w-full max-w-36" />
94
+ <utils.components.Skeleton class="h-8 w-full max-w-60" />
95
+ <utils.components.Skeleton class="h-4 w-full max-w-96" />
96
+ </div>
97
+ <div class="flex flex-col gap-2">
98
+ <utils.components.Skeleton class="h-4 w-full max-w-36" />
99
+ <div class="flex gap-4">
100
+ <utils.components.Skeleton class="h-8 w-full max-w-60" />
101
+ <utils.components.Skeleton class="h-8 w-full max-w-60" />
102
+ </div>
103
+ <utils.components.Skeleton class="h-4 w-full max-w-96" />
104
+ </div>
105
+ </div>
106
+ {/if}
@@ -0,0 +1,14 @@
1
+ import { SvelteComponentTyped } from "svelte";
2
+ declare const __propDef: {
3
+ props: Record<string, never>;
4
+ events: {
5
+ [evt: string]: CustomEvent<any>;
6
+ };
7
+ slots: {};
8
+ };
9
+ export type AccountProps = typeof __propDef.props;
10
+ export type AccountEvents = typeof __propDef.events;
11
+ export type AccountSlots = typeof __propDef.slots;
12
+ export default class Account extends SvelteComponentTyped<AccountProps, AccountEvents, AccountSlots> {
13
+ }
14
+ export {};
@@ -0,0 +1,87 @@
1
+ <script lang="ts">
2
+ import type { ExtensionProps } from "@lobb-js/studio";
3
+ import { onMount } from "svelte";
4
+
5
+ const { utils }: ExtensionProps = $props();
6
+
7
+ let user = $state({
8
+ name: "",
9
+ });
10
+ const icons = utils.components.Icons;
11
+
12
+ // @ts-ignore: Im sure the session object exist
13
+ const currentUserId = utils.ctx.extensions.auth.session.user.id;
14
+ let loaded = $state(false);
15
+ onMount(async () => {
16
+ const response = await utils.lobb.findOne("auth_users", currentUserId);
17
+ const result = await response.json();
18
+ user = result.data;
19
+ loaded = true;
20
+ });
21
+
22
+ async function onUpdateCLick() {
23
+ // TODO: add the logic of updating some of the current user's information
24
+ const updateObject: any = {};
25
+ updateObject["name"] = user.name;
26
+ const response = await utils.lobb.updateOne(
27
+ "auth_users",
28
+ currentUserId,
29
+ updateObject,
30
+ );
31
+ if (response.status >= 400) {
32
+ const result = await response.json();
33
+ utils.toast.error(result.message);
34
+ return;
35
+ }
36
+ utils.toast.success("Profile updated successfully.");
37
+ }
38
+ </script>
39
+
40
+ {#if loaded}
41
+ <div class="flex max-w-[40rem] flex-1 flex-col gap-4 overflow-y-visible">
42
+ <div>
43
+ <div class="text-lg font-semibold">Profile</div>
44
+ <div class="text-sm text-muted-foreground">
45
+ Edit information that others will see.
46
+ </div>
47
+ </div>
48
+ <utils.components.Separator />
49
+ <!-- TODO: you should add name and other stuff like phone number and stuff like that -->
50
+ <div class="flex flex-col gap-2">
51
+ <div class="text-sm">Full Name</div>
52
+ <utils.components.Input
53
+ placeholder="Full Name"
54
+ bind:value={user.name}
55
+ />
56
+ <div class="text-xs text-muted-foreground">
57
+ This is your public display name. It can be your real name or a
58
+ pseudonym.
59
+ </div>
60
+ </div>
61
+ <utils.components.Button
62
+ class="self-start"
63
+ onclick={onUpdateCLick}
64
+ Icon={icons.Pencil}
65
+ >
66
+ Update Profile
67
+ </utils.components.Button>
68
+ </div>
69
+ {:else}
70
+ <div class="flex max-w-[40rem] flex-1 flex-col gap-8 overflow-y-visible">
71
+ <div class="grid gap-2">
72
+ <utils.components.Skeleton class="h-8 w-full max-w-60" />
73
+ <utils.components.Skeleton class="h-4 w-full max-w-96" />
74
+ </div>
75
+ <utils.components.Separator />
76
+ <div class="flex flex-col gap-2">
77
+ <utils.components.Skeleton class="h-4 w-full max-w-36" />
78
+ <utils.components.Skeleton class="h-8 w-full max-w-60" />
79
+ <utils.components.Skeleton class="h-4 w-full max-w-96" />
80
+ </div>
81
+ <div class="flex flex-col gap-2">
82
+ <utils.components.Skeleton class="h-4 w-full max-w-36" />
83
+ <utils.components.Skeleton class="h-8 w-full max-w-60" />
84
+ <utils.components.Skeleton class="h-4 w-full max-w-96" />
85
+ </div>
86
+ </div>
87
+ {/if}
@@ -0,0 +1,14 @@
1
+ import { SvelteComponentTyped } from "svelte";
2
+ declare const __propDef: {
3
+ props: Record<string, never>;
4
+ events: {
5
+ [evt: string]: CustomEvent<any>;
6
+ };
7
+ slots: {};
8
+ };
9
+ export type ProfileProps = typeof __propDef.props;
10
+ export type ProfileEvents = typeof __propDef.events;
11
+ export type ProfileSlots = typeof __propDef.slots;
12
+ export default class Profile extends SvelteComponentTyped<ProfileProps, ProfileEvents, ProfileSlots> {
13
+ }
14
+ export {};
@@ -0,0 +1,48 @@
1
+ <script lang="ts">
2
+ import type { ExtensionProps } from "@lobb-js/studio";
3
+ import Profile from "./components/profile.svelte";
4
+ import Account from "./components/account.svelte";
5
+
6
+ const props: ExtensionProps = $props();
7
+ const components = props.utils.components;
8
+ const isSmall = $derived(!props.utils.mediaQueries.sm.current);
9
+
10
+ let selectedView: "profile" | "account" = $state("profile");
11
+ </script>
12
+
13
+ <div class="flex flex-col gap-4 p-4">
14
+ <div>
15
+ <div class="text-xl font-bold">User Settings</div>
16
+ <div class="text-sm text-muted-foreground">
17
+ Manage your user settings.
18
+ </div>
19
+ </div>
20
+ <components.Separator />
21
+ <div class="flex gap-4 {isSmall ? "flex-col" : ""}">
22
+ <div class="flex w-64 {isSmall ? "" : "flex-col"}">
23
+ <components.Button
24
+ onclick={() => (selectedView = "profile")}
25
+ variant="ghost"
26
+ class="flex justify-start text-muted-foreground hover:underline"
27
+ >
28
+ Profile
29
+ </components.Button>
30
+ <components.Button
31
+ onclick={() => (selectedView = "account")}
32
+ variant="ghost"
33
+ class="flex justify-start text-muted-foreground hover:underline"
34
+ >
35
+ Account
36
+ </components.Button>
37
+ </div>
38
+ {#if selectedView === "profile"}
39
+ <Profile {...props} />
40
+ {:else if selectedView === "account"}
41
+ <Account {...props} />
42
+ {:else}
43
+ <div class="font-bold text-red-500">
44
+ The "{selectedView}" view doesnt exist
45
+ </div>
46
+ {/if}
47
+ </div>
48
+ </div>
@@ -0,0 +1,14 @@
1
+ import { SvelteComponentTyped } from "svelte";
2
+ declare const __propDef: {
3
+ props: Record<string, never>;
4
+ events: {
5
+ [evt: string]: CustomEvent<any>;
6
+ };
7
+ slots: {};
8
+ };
9
+ export type IndexProps = typeof __propDef.props;
10
+ export type IndexEvents = typeof __propDef.events;
11
+ export type IndexSlots = typeof __propDef.slots;
12
+ export default class Index extends SvelteComponentTyped<IndexProps, IndexEvents, IndexSlots> {
13
+ }
14
+ export {};
File without changes
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ // place files you want to import through the `$lib` alias in this folder.