@wishbone-media/spark 0.5.2 → 0.6.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wishbone-media/spark",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -42,7 +42,8 @@
42
42
  "./formkit.config.js": "./formkit.config.js",
43
43
  "./formkit.theme.mjs": "./formkit.theme.mjs",
44
44
  "./assets/css/*": "./src/assets/css/*",
45
- "./assets/fonts/*": "./src/assets/fonts/*"
45
+ "./assets/fonts/*": "./src/assets/fonts/*",
46
+ "./assets/images/brands/*": "./src/assets/images/brands/*"
46
47
  },
47
48
  "publishConfig": {
48
49
  "access": "public"
@@ -12,9 +12,15 @@
12
12
  />
13
13
  </div>
14
14
  </div>
15
+ <div
16
+ v-if="sparkBrandFilterStore.allBrands.length === 0"
17
+ class="flex px-[22px] py-[15px] text-gray-500 text-sm"
18
+ >
19
+ No brands configured
20
+ </div>
15
21
  <div
16
22
  v-for="brand in sparkBrandFilterStore.allBrands"
17
- :key="brand.name"
23
+ :key="brand.id"
18
24
  :class="brand.current ? 'bg-gray-50' : 'hover:bg-gray-50'"
19
25
  class="flex px-[22px] py-[15px] cursor-pointer"
20
26
  @click="selectBrand(brand)"
@@ -1,31 +1,55 @@
1
1
  import { defineStore } from 'pinia'
2
2
  import { computed, reactive } from 'vue'
3
3
 
4
- import mrPestControllerLogo from '@/assets/images/mr-pest-controller.png'
5
- import mrGutterCleaningLogo from '@/assets/images/mr-gutter-cleaning.png'
6
- import mrAntennaLogo from '@/assets/images/mr-antenna.png'
7
-
8
4
  export const useSparkBrandFilterStore = defineStore('brandFilter', () => {
9
5
  const state = reactive({
10
- brands: [
11
- {
12
- name: 'Mr Pest Controller',
13
- logo: mrPestControllerLogo,
14
- current: false,
15
- },
16
- {
17
- name: 'Mr Gutter Cleaning',
18
- logo: mrGutterCleaningLogo,
19
- current: false,
20
- },
21
- { name: 'Mr Antenna', logo: mrAntennaLogo, current: true },
22
- ],
6
+ brands: [],
23
7
  })
24
8
 
25
- const currentBrand = computed(() => state.brands.find((item) => item.current))
9
+ const initialize = (config = {}) => {
10
+ if (!config.brands || !Array.isArray(config.brands)) {
11
+ console.warn('useSparkBrandFilterStore: No brands provided to initialize()')
12
+ state.brands = []
13
+ return
14
+ }
15
+
16
+ // Validate brand structure
17
+ const validBrands = config.brands.filter((brand) => {
18
+ const isValid = brand.id && brand.name && brand.logo
19
+ if (!isValid) {
20
+ console.warn('useSparkBrandFilterStore: Invalid brand object', brand)
21
+ }
22
+ return isValid
23
+ })
24
+
25
+ // Ensure exactly one brand is marked as current
26
+ const currentBrands = validBrands.filter((b) => b.current)
27
+ if (currentBrands.length === 0 && validBrands.length > 0) {
28
+ validBrands[0].current = true
29
+ } else if (currentBrands.length > 1) {
30
+ // Only the first current brand stays current
31
+ validBrands.forEach((brand) => {
32
+ brand.current = brand === currentBrands[0]
33
+ })
34
+ }
35
+
36
+ state.brands = validBrands.map((brand) => ({
37
+ id: brand.id,
38
+ name: brand.name,
39
+ logo: brand.logo,
40
+ current: brand.current || false,
41
+ }))
42
+ }
43
+
44
+ const currentBrand = computed(() => state.brands.find((item) => item.current) || null)
26
45
  const allBrands = computed(() => state.brands)
27
46
 
28
47
  const toggleBrand = (brand) => {
48
+ if (!brand || !state.brands.includes(brand)) {
49
+ console.warn('useSparkBrandFilterStore: Invalid brand provided to toggleBrand()')
50
+ return
51
+ }
52
+
29
53
  state.brands.forEach((item) => {
30
54
  item.current = item === brand
31
55
  })
@@ -33,6 +57,7 @@ export const useSparkBrandFilterStore = defineStore('brandFilter', () => {
33
57
 
34
58
  return {
35
59
  state,
60
+ initialize,
36
61
  currentBrand,
37
62
  allBrands,
38
63
  toggleBrand,
@@ -0,0 +1,75 @@
1
+ <template>
2
+ <div class="h-full grid place-content-center relative">
3
+ <div class="absolute top-8 left-8">
4
+ <svg
5
+ width="59"
6
+ height="23"
7
+ viewBox="0 0 59 23"
8
+ fill="none"
9
+ xmlns="http://www.w3.org/2000/svg"
10
+ >
11
+ <path
12
+ d="M49.2029 17.1264V8.03835H44.0829V5.22235H58.0989V8.03835H52.9629V17.1264H49.2029Z"
13
+ fill="#1C64F2"
14
+ />
15
+ <path d="M34.5 5.22235H38.228V14.1664H46.5V17.1264H34.5V5.22235Z" fill="#1C64F2" />
16
+ <path
17
+ d="M28.3161 0C29.1499 0 29.7522 0.798785 29.5209 1.59757L27.1856 9.77748H30.9046C31.747 9.77748 32.4279 10.4584 32.4279 11.3008C32.4279 11.7504 32.2315 12.1738 31.891 12.4619L20.5989 22.0517C20.3719 22.2438 20.0839 22.3485 19.787 22.3485C18.9533 22.3485 18.351 21.5497 18.5823 20.751L20.9176 12.571H17.1463C16.33 12.571 15.6709 11.9119 15.6709 11.1001C15.6709 10.6679 15.8586 10.262 16.186 9.98263L27.5043 0.301181C27.7312 0.104759 28.0193 0 28.3161 0ZM26.7404 3.71021L18.8311 10.4759H22.3056C22.633 10.4759 22.9429 10.6286 23.1437 10.8905C23.3445 11.1524 23.4056 11.4929 23.3139 11.8072L21.3584 18.6601L29.355 11.8727H25.7976C25.4702 11.8727 25.1603 11.7199 24.9595 11.458C24.7587 11.1961 24.6976 10.8556 24.7893 10.5413L26.7404 3.71021Z"
18
+ fill="#1C64F2"
19
+ />
20
+ <path
21
+ d="M0 17.1264V5.22235H10.192C13.6 5.22235 14.544 6.53435 14.544 7.94235V8.16635C14.544 9.70235 13.232 10.3264 12.656 10.4864C13.472 10.6944 15.216 11.3984 15.216 13.4784V13.7024C15.216 15.5904 14.144 17.1264 10.288 17.1264H0ZM9.552 7.73435H3.728V9.67035H9.552C10.592 9.67035 10.848 9.19035 10.848 8.71035V8.67835C10.848 8.18235 10.592 7.73435 9.552 7.73435ZM9.872 12.1984H3.728V14.5344H9.872C11.12 14.5344 11.344 13.8464 11.344 13.3664V13.3024C11.344 12.7904 11.104 12.1984 9.872 12.1984Z"
22
+ fill="#1C64F2"
23
+ />
24
+ </svg>
25
+ </div>
26
+
27
+ <div class="max-w-sm grid gap-y-1 -mt-8">
28
+ <div class="mb-7">
29
+ <h1 class="text-4xl text-gray-900 semibold tracking-tight mb-3">Log in</h1>
30
+
31
+ <p class="text-gray-600">
32
+ Welcome back{{ appStore.state.app ? ` to ${appStore.state.app}` : '' }}! Please enter
33
+ your details.
34
+ </p>
35
+ </div>
36
+
37
+ <FormKit type="form" @submit="handleLogin" :actions="false">
38
+ <FormKit
39
+ label="Email"
40
+ name="email"
41
+ placeholder="Enter your email"
42
+ type="email"
43
+ validation="required|email"
44
+ outer-class="max-w-full"
45
+ />
46
+
47
+ <FormKit
48
+ label="Password"
49
+ name="password"
50
+ placeholder="••••••••"
51
+ type="password"
52
+ validation="required"
53
+ outer-class="max-w-full"
54
+ />
55
+
56
+ <div class="grid grid-flow-col justify-between my-1">
57
+ <FormKit label="Remember me" name="remember" type="checkbox" />
58
+ </div>
59
+
60
+ <div v-if="errorMessage" class="text-red-600 text-sm mb-2">
61
+ {{ errorMessage }}
62
+ </div>
63
+
64
+ <spark-button type="submit" size="xl" :disabled="isLoading" button-class="w-full mb-2">
65
+ <span v-if="!isLoading">Sign in</span>
66
+ <span v-else>Signing in...</span>
67
+ </spark-button>
68
+ </FormKit>
69
+ </div>
70
+ </div>
71
+ </template>
72
+
73
+ <script setup>
74
+ import { SparkButton } from '@/index.js'
75
+ </script>
@@ -31,8 +31,8 @@
31
31
  <h1 class="text-4xl text-gray-900 semibold tracking-tight mb-3">Log in</h1>
32
32
 
33
33
  <p class="text-gray-600">
34
- Welcome back{{ props.appName ? ` to ${props.appName}` : '' }}! Please enter your
35
- details.
34
+ Welcome back{{ appStore.state.app ? ` to ${appStore.state.app}` : '' }}! Please enter
35
+ your details.
36
36
  </p>
37
37
  </div>
38
38
 
@@ -70,35 +70,26 @@
70
70
  {{ errorMessage }}
71
71
  </div>
72
72
 
73
- <spark-button type="submit" size="xl" :disabled="isLoading" button-class="w-full">
73
+ <spark-button type="submit" size="xl" :disabled="isLoading" button-class="w-full mb-2">
74
74
  <span v-if="!isLoading">Sign in</span>
75
75
  <span v-else>Signing in...</span>
76
76
  </spark-button>
77
77
  </FormKit>
78
-
79
- <div v-if="props.signupRoute" class="mt-4 text-center text-sm text-gray-600">
80
- Don't have an account?
81
- <router-link :to="props.signupRoute" class="text-primary-600 font-semibold">
82
- Sign up
83
- </router-link>
84
- </div>
85
78
  </div>
86
79
  </div>
87
80
  </template>
88
81
 
89
82
  <script setup>
90
83
  import { ref } from 'vue'
91
- import { SparkButton } from '@/index.js'
84
+ import { SparkButton, useSparkAppStore } from '@/index.js'
85
+
86
+ const appStore = useSparkAppStore()
92
87
 
93
88
  const props = defineProps({
94
89
  logo: {
95
90
  type: String,
96
91
  default: '',
97
92
  },
98
- appName: {
99
- type: String,
100
- default: '',
101
- },
102
93
  onLogin: {
103
94
  type: Function,
104
95
  required: true,
@@ -107,10 +98,6 @@ const props = defineProps({
107
98
  type: String,
108
99
  default: '/forgot-password',
109
100
  },
110
- signupRoute: {
111
- type: String,
112
- default: '',
113
- },
114
101
  })
115
102
 
116
103
  const emit = defineEmits(['login-success', 'login-error'])
@@ -0,0 +1,15 @@
1
+ <template></template>
2
+
3
+ <script setup>
4
+ import { onMounted } from 'vue'
5
+
6
+ const props = defineProps({
7
+ onLogout: {
8
+ type: Function,
9
+ },
10
+ })
11
+
12
+ onMounted(async () => {
13
+ await props.onLogout()
14
+ });
15
+ </script>