edvoyui-component-library-test-flight 0.0.147 → 0.0.149

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.
@@ -1,5 +1,5 @@
1
1
  export * from "/Volumes/work/repos/edvoy-ui-v2/src/components/modal/EUIModal.vue?vue&type=script&lang.ts";
2
- import "/Volumes/work/repos/edvoy-ui-v2/src/components/modal/EUIModal.vue?vue&type=style&index=0&scoped=0a1278e7&lang.scss";
2
+ import "/Volumes/work/repos/edvoy-ui-v2/src/components/modal/EUIModal.vue?vue&type=style&index=0&scoped=6e392f5b&lang.scss";
3
3
  declare const _default: any;
4
4
  export default _default;
5
5
  //# sourceMappingURL=EUIModal.vue.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"EUIModal.vue.d.ts","sourceRoot":"","sources":["../../src/components/modal/EUIModal.vue"],"names":[],"mappings":"AACA,cAAc,2FAA2F,CAAC;AA6F1G,OAAO,oHAAoH,CAAC;;AAE5H,wBAAmH"}
1
+ {"version":3,"file":"EUIModal.vue.d.ts","sourceRoot":"","sources":["../../src/components/modal/EUIModal.vue"],"names":[],"mappings":"AACA,cAAc,2FAA2F,CAAC;AA+F1G,OAAO,oHAAoH,CAAC;;AAE5H,wBAAmH"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "edvoyui-component-library-test-flight",
3
3
  "private": false,
4
- "version": "0.0.147",
4
+ "version": "0.0.149",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "dist/",
@@ -17,10 +17,18 @@
17
17
  <div class="h-[clac(100svh-64px)] w-full px-10 py-8 max-w-screen-xl mx-auto">
18
18
  <h1 class="mb-2 font-semibold text-gray-900 tetx-lg">Edvoy UI Componnet</h1>
19
19
 
20
- <pre class="p-2 text-red-500 text-xxs">
21
- {{ popoverSelect }}
22
- {{ businessAreaSel }}
23
- </pre>
20
+ <div class="relative">
21
+ <EUIStepperHorizontal
22
+ :steps="allSteps"
23
+ :step-status="currentStage"
24
+ :history="lifeCycleHistory"
25
+ />
26
+ </div>
27
+
28
+
29
+
30
+
31
+
24
32
  <div class="grid max-w-2xl grid-cols-2 gap-6 mx-auto">
25
33
  <EUISelect
26
34
  v-model="businessAreaSel"
@@ -1457,6 +1465,7 @@ import EUIButtonGroup from "./button/EUIButtonGroup.vue";
1457
1465
  import EUISearchExpand from "./searchexpand/EUISearchExpand.vue";
1458
1466
  import EUITabOutline from "./tabs/EUITabOutline.vue";
1459
1467
  import EUIPopover from "./popover/EUIPopover.vue";
1468
+ import EUIStepperHorizontal from "./stepperTimeline/EUIStepperHorizontal.vue";
1460
1469
 
1461
1470
  const checkboxData = ref([])
1462
1471
  const businessAreaSel = ref()
@@ -1604,6 +1613,33 @@ const handleActiveItem = (activeItems: number) => {
1604
1613
  console.log("Currently active accordion(s):", activeItems);
1605
1614
  };
1606
1615
 
1616
+ // Stepper dynamic Data
1617
+ const allSteps = ['MQL','SAL','Prospect','Customer']
1618
+ const currentStage = 'Prospect'
1619
+ const lifeCycleHistory = [
1620
+ {
1621
+ "lifecycleStage": "MQL",
1622
+ "updatedAt": "2025-08-25T15:35:55.965Z",
1623
+ "updatedBy": {
1624
+ "email": "testuser3@edvoy.com"
1625
+ }
1626
+ },
1627
+ {
1628
+ "lifecycleStage": "SAL",
1629
+ "updatedAt": "2025-08-25T15:36:16.616Z",
1630
+ "updatedBy": {
1631
+ "email": "testuser3@edvoy.com"
1632
+ }
1633
+ },
1634
+ {
1635
+ "lifecycleStage": "PROSPECT",
1636
+ "updatedAt": "2025-08-25T15:42:30.195Z",
1637
+ "updatedBy": {
1638
+ "email": "testuser2@edvoy.com"
1639
+ }
1640
+ }
1641
+ ]
1642
+
1607
1643
  //TODO: Select Data
1608
1644
  const datas = ref([
1609
1645
  {
@@ -15,7 +15,7 @@
15
15
  'relative',
16
16
  { 'pointer-events-none cursor-not-allowed': disabled },
17
17
  {
18
- 'h-14 rounded-2xl focus-within:border-purple-600 focus-within:ring-1 focus-within:ring-purple-600 border border-gray-100':
18
+ 'h-14 rounded-2xl focus-within:border-purple-600 focus-within:ring-1 focus-within:ring-purple-600 border border-gray-200':
19
19
  inputFilled,
20
20
  },
21
21
  'group cursor-pointer relative w-full mb-2 overflow-hidden',
@@ -77,7 +77,7 @@
77
77
  'z-10 block placeholder:text-gray-400 focus:outline-none text-sm font-medium appearance-none disabled:opacity-75 autofill:bg-white leading-6 transition-all duration-100 border-none outline-none',
78
78
  inputFilled
79
79
  ? 'pt-6 pb-3 rounded-2xl size-full'
80
- : 'py-3 h-10 w-full ring-1 ring-gray-100 focus-within:ring-purple-600 focus-within:ring-2 ring-inset',
80
+ : 'py-3 h-10 w-full ring-1 ring-gray-200 focus-within:ring-purple-600 focus-within:ring-2 ring-inset',
81
81
  !inputFilled && rounded ? 'rounded-2xl' : 'rounded-md',
82
82
  disabled ? 'cursor-not-allowed' : 'cursor-text',
83
83
  getIconClass(),
@@ -55,6 +55,20 @@ const meta: Meta<typeof EUIModal> = {
55
55
  defaultValue: { summary: "sm" },
56
56
  },
57
57
  },
58
+ placement: {
59
+ control: { type: "select" },
60
+ options: [
61
+ "top",
62
+ "bottom",
63
+ "center",
64
+ ],
65
+ description:
66
+ "Defines the position of the modal show in 'placement' element. Default is 'center'.",
67
+ defaultValue: "center",
68
+ table: {
69
+ defaultValue: { summary: "center" },
70
+ },
71
+ },
58
72
  },
59
73
  },
60
74
  parameters: {
@@ -3,7 +3,14 @@
3
3
  <Transition name="modal" appear>
4
4
  <div
5
5
  v-if="isVisible"
6
- class="fixed z-50 flex flex-col items-center justify-end inset-2 sm:justify-center"
6
+ class="fixed z-50 flex flex-col items-center justify-end inset-2 modal-wrapper"
7
+ :class="[
8
+ placement === 'top'
9
+ ? 'sm:justify-start'
10
+ : placement === 'bottom'
11
+ ? 'sm:justify-end'
12
+ : 'sm:justify-center',
13
+ ]"
7
14
  @click.self="closeModal"
8
15
  >
9
16
  <div
@@ -11,10 +18,12 @@
11
18
  ></div>
12
19
  <div
13
20
  :class="[
14
- 'bg-white shadow-lg w-full overflow-hidden relative flex flex-col justify-between',
21
+ 'bg-white shadow-lg w-full overflow-hidden relative flex flex-col justify-between modal-container',
15
22
  slideClass,
16
23
  roundedClass !== '' ? roundedClass : 'rounded-t-3xl md:rounded-2xl',
17
- size === 'full' ? 'h-full max-h-svh' : 'max-h-[calc(100svh-3rem)] md:h-auto'
24
+ size === 'full'
25
+ ? 'h-full max-h-svh'
26
+ : 'max-h-[calc(100svh-3rem)] md:h-auto',
18
27
  ]"
19
28
  >
20
29
  <template v-if="$slots.header">
@@ -57,14 +66,20 @@
57
66
  <template v-if="$slots.content">
58
67
  <slot name="content"></slot>
59
68
  </template>
60
- <div v-else class="p-4 border-t border-b border-gray-200 max-h-[calc(100svh-3rem)] flex-1">
69
+ <div
70
+ v-else
71
+ class="p-4 border-t border-b border-gray-200 max-h-[calc(100svh-3rem)] flex-1"
72
+ >
61
73
  <slot></slot>
62
74
  </div>
63
75
 
64
76
  <template v-if="$slots.footer">
65
77
  <slot name="footer"></slot>
66
78
  </template>
67
- <div v-else class="sticky bottom-0 flex items-center justify-end float-none p-4 space-x-2">
79
+ <div
80
+ v-else
81
+ class="sticky bottom-0 flex items-center justify-end float-none p-4 space-x-2"
82
+ >
68
83
  <button
69
84
  @click="closeModal"
70
85
  class="px-4 py-2 text-base font-semibold tracking-wide text-gray-600 transition-colors duration-75 bg-white rounded-md hover:bg-gray-100"
@@ -125,6 +140,10 @@ export default defineComponent({
125
140
  type: String as PropType<"xs" | "sm" | "md" | "lg" | "xl" | "full">,
126
141
  default: "sm",
127
142
  },
143
+ placement: {
144
+ type: String as PropType<"center" | "bottom" | "top">,
145
+ default: "center",
146
+ },
128
147
  },
129
148
  emits: ["update:isVisible", "confirm"],
130
149
  setup(props, { emit }) {
@@ -1,112 +1,278 @@
1
1
  <template>
2
- <nav aria-label="Progress" class="relative z-10">
3
- <ol role="list" class="flex flex-row items-start justify-center w-full py-4 ps-4 pe-2">
4
- <li
5
- v-for="(step, stepIdx) in lifeCycleEntries"
6
- :key="step.value"
7
- :class="[
8
- stepIdx !== lifeCycleEntries.length - 1 ? 'pe-8' : '',
9
- 'relative flex items-center w-full pt-6 group',
10
- getStatus(step.key, stepIdx) === 'upcoming'
11
- ? 'pointer-events-none'
12
- : '',
13
- ]"
2
+ <div>
3
+ <nav aria-label="Progress" class="relative z-50 h-16 max-w-full mx-auto">
4
+ <ol
5
+ role="list"
6
+ class="flex flex-row items-start justify-center w-full py-2 ps-2 pe-2"
14
7
  >
15
- <template v-if="getStatus(step.key, stepIdx) === 'complete'">
16
- <div
17
- v-if="stepIdx !== lifeCycleEntries.length - 1"
18
- class="absolute bg-green-500 w-full start-0 top-2.5 h-[0.0875rem] z-0"
19
- />
20
- <div class="absolute top-0 start-0 flex items-center size-5 bg-green-500 rounded-full border-[1.5px] border-green-500" >
21
- <svg fill="currentColor" width="16" height="16" viewBox="0 0 24 24" class="text-white size-4"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"><title>Check icon</title></path></svg>
22
- </div>
23
- <div class="flex flex-col items-start justify-between max-w-full w-full py-0.5">
24
- <span class="text-xs font-semibold text-gray-900">{{ startCase(step.value) }}</span>
25
- <span class="text-xs font-medium text-gray-500">{{'26-05-2024 18:47'}}</span>
26
- </div>
27
- </template>
28
- <template v-else-if="getStatus(step.key, stepIdx) === 'current'">
29
- <div
30
- class="absolute bg-orange-500 w-full start-0 top-2.5 h-[0.0875rem] z-0"
31
- />
32
- <div class="absolute start-0 top-0 flex items-center justify-center size-5 rounded-full bg-white border-[1.5px] border-orange-500">
33
- <div class="size-2.5 bg-orange-400 rounded-full" />
34
- </div>
35
- <div class="flex flex-col items-start justify-between max-w-full w-full py-0.5">
36
- <span class="text-xs font-semibold text-gray-900">{{ startCase(step.value) }}</span>
37
- <span class="text-xs font-medium text-gray-500">{{'26-05-2024 18:47'}}</span>
38
- </div>
39
- </template>
40
- <template v-else-if="getStatus(step.key, stepIdx) === 'upcoming'">
41
- <div
42
- v-if="stepIdx !== lifeCycleEntries.length - 1"
43
- class="absolute bg-gray-300 w-full start-0 top-2.5 h-[0.0875rem] z-0"
44
- />
45
- <div class="absolute top-0 start-0 flex items-center justify-center size-5 rounded-full bg-gray-100 border-gray-300 border-[1.5px]">
46
- <div class="bg-gray-200 rounded-full size-3" />
47
- </div>
48
- <div class="flex flex-col items-start justify-between max-w-full w-full py-0.5">
49
- <span class="text-xs font-semibold text-gray-900">{{ startCase(step.value) }}</span>
50
- <span class="text-xs font-medium text-gray-500">{{'26-05-2024 18:47'}}</span>
51
- </div>
52
- </template>
53
- </li>
54
- </ol>
55
- </nav>
8
+ <li
9
+ v-for="(step, stepIdx) in lifeCycleEntries"
10
+ :key="step.value"
11
+ :class="[
12
+ stepIdx !== lifeCycleEntries.length - 1 ? 'pe-6' : '',
13
+ 'relative flex items-center w-full max-w-36 pt-6 group',
14
+ getStatus(step.key, stepIdx) === 'upcoming'
15
+ ? 'pointer-events-none'
16
+ : 'cursor-pointer',
17
+ ]"
18
+ >
19
+ <template v-if="getStatus(step.key, stepIdx) === 'complete'">
20
+ <div
21
+ v-if="stepIdx !== lifeCycleEntries.length - 1"
22
+ class="absolute bg-green-500 w-full start-0 top-2.5 h-[0.0875rem] z-0"
23
+ />
24
+ <div
25
+ class="absolute top-0 flex items-center text-green-500 bg-white rounded-full start-0 size-5"
26
+ >
27
+ <svg
28
+ width="22"
29
+ height="22"
30
+ viewBox="0 0 22 22"
31
+ fill="none"
32
+ xmlns="http://www.w3.org/2000/svg"
33
+ class="transform scale-110"
34
+ >
35
+ <path
36
+ fill-rule="evenodd"
37
+ clip-rule="evenodd"
38
+ d="M0.849609 10.9996C0.849609 5.39392 5.39392 0.849609 10.9996 0.849609C16.6053 0.849609 21.1496 5.39392 21.1496 10.9996C21.1496 16.6053 16.6053 21.1496 10.9996 21.1496C5.39392 21.1496 0.849609 16.6053 0.849609 10.9996ZM15.0634 9.67153C15.5195 9.36008 15.6368 8.73787 15.3253 8.28178C15.0139 7.82569 14.3917 7.70843 13.9356 8.01988L13.8343 8.08904C12.1934 9.20953 10.779 10.6239 9.66046 12.257L8.20624 10.8044C7.81551 10.4141 7.18234 10.4144 6.79203 10.8051C6.40171 11.1959 6.40205 11.829 6.79278 12.2193L9.13398 14.558C9.35437 14.7782 9.66477 14.8831 9.97353 14.8417C10.2823 14.8003 10.5541 14.6174 10.7088 14.347C11.7597 12.5094 13.214 10.9344 14.9621 9.7407L15.0634 9.67153Z"
39
+ fill="currentColor"
40
+ />
41
+ </svg>
42
+ </div>
43
+ <div
44
+ class="flex flex-col items-start justify-between w-full max-w-full p-1 transition-opacity duration-200 ease-in group-hover:invisible group-hover:opacity-0"
45
+ >
46
+ <slot name="default">
47
+ <span class="text-xs font-semibold text-gray-900">{{
48
+ startCase(step.value)
49
+ }}</span>
50
+ </slot>
51
+ </div>
52
+
53
+ <div
54
+ class="absolute left-0 flex flex-col items-start justify-between invisible max-w-full p-2 break-all transition-all duration-300 ease-in-out origin-top-left translate-y-1 bg-white rounded-md shadow-md opacity-0 group-hover:visible group-hover:opacity-100 w-44 top-6 group-hover:pointer-events-auto group-hover:translate-y-0"
55
+ >
56
+ <span class="text-xs font-semibold text-gray-900">
57
+ <slot name="title">
58
+ {{ startCase(step.value) }}
59
+ </slot>
60
+ </span>
61
+ <span class="font-medium text-gray-700 text-xss">
62
+ <slot name="date">
63
+ {{ getFormattedDate(step.value) }}
64
+ </slot>
65
+ </span>
66
+ <span class="font-medium text-gray-500 text-xss">
67
+ <slot name="updatedBy">
68
+ {{ getUpdatedByEmail(step.value) }}
69
+ </slot>
70
+ </span>
71
+ </div>
72
+ </template>
73
+ <template v-else-if="getStatus(step.key, stepIdx) === 'current'">
74
+ <div
75
+ class="absolute bg-orange-500 w-full start-0 top-2.5 h-[0.0875rem] z-0"
76
+ />
77
+ <div
78
+ class="absolute start-0 top-0 flex items-center justify-center size-5 rounded-full bg-white border-[1.5px] border-orange-500"
79
+ >
80
+ <div class="size-2.5 bg-orange-400 rounded-full" />
81
+ </div>
82
+ <div
83
+ class="flex flex-col items-start justify-between w-full max-w-full p-1 transition-opacity duration-200 ease-in-out group-hover:invisible group-hover:opacity-0"
84
+ >
85
+ <slot name="default">
86
+ <span class="text-xs font-semibold text-gray-900"
87
+ >{{ startCase(step.value) }}
88
+ </span>
89
+ </slot>
90
+ </div>
91
+ <div
92
+ class="absolute left-0 flex flex-col items-start justify-between invisible max-w-full p-2 break-all transition-all duration-300 ease-in-out origin-top-left translate-y-1 bg-white rounded-md shadow-md opacity-0 group-hover:visible group-hover:opacity-100 w-44 top-6 group-hover:pointer-events-auto group-hover:translate-y-0"
93
+ >
94
+ <span class="text-xs font-semibold text-gray-900">
95
+ <slot name="title">
96
+ {{ startCase(step.value) }}
97
+ </slot>
98
+ </span>
99
+ <span class="font-medium text-gray-700 text-xss">
100
+ <slot name="date">
101
+ {{ getFormattedDate(step.value) }}
102
+ </slot>
103
+ </span>
104
+ <span class="font-medium text-gray-500 text-xss">
105
+ <slot name="updatedBy">
106
+ {{ getUpdatedByEmail(step.value) }}
107
+ </slot>
108
+ </span>
109
+ </div>
110
+ </template>
111
+ <template v-else-if="getStatus(step.key, stepIdx) === 'upcoming'">
112
+ <div
113
+ v-if="stepIdx !== lifeCycleEntries.length - 1"
114
+ class="absolute bg-gray-300 w-full start-0 top-2.5 h-[0.0875rem] z-0"
115
+ />
116
+ <div
117
+ class="absolute top-0 start-0 flex items-center justify-center size-5 rounded-full bg-gray-100 border-gray-300 border-[1.5px]"
118
+ >
119
+ <div class="bg-gray-200 rounded-full size-3" />
120
+ </div>
121
+ <div
122
+ class="flex flex-col items-start justify-between w-full max-w-full p-1"
123
+ >
124
+ <slot name="default">
125
+ <span
126
+ class="text-xs font-semibold text-gray-900 whitespace-nowrap"
127
+ >{{ startCase(step.value) }}
128
+ </span>
129
+ </slot>
130
+ </div>
131
+ </template>
132
+ </li>
133
+ </ol>
134
+ </nav>
135
+ </div>
56
136
  </template>
57
137
  <script setup lang="ts">
58
- import { startCase } from 'lodash';
59
- import { computed, PropType } from 'vue';
138
+ import { startCase } from "lodash";
139
+ import { computed, PropType } from "vue";
60
140
 
61
141
  const props = defineProps({
62
- stepStatus: {
63
- type: String as PropType<"contact" | "MQL" | 'SQL' | 'opportunity' | 'Prospect' | 'Prospect Paid' | 'Prospect Enrolled' | 'Customer'>,
64
- default: 'contact'
65
- }
66
- })
142
+ stepStatus: {
143
+ type: String as PropType<
144
+ | "contact"
145
+ | "MQL"
146
+ | "SAL"
147
+ | "SQL"
148
+ | "opportunity"
149
+ | "Prospect"
150
+ | "Prospect Paid"
151
+ | "Prospect Enrolled"
152
+ | "Customer"
153
+ >,
154
+ default: "contact",
155
+ },
156
+ steps: {
157
+ type: Array as PropType<string[]>,
158
+ default: () => [
159
+ "contact",
160
+ "MQL",
161
+ "SAL",
162
+ "SQL",
163
+ "opportunity",
164
+ "Prospect",
165
+ "Prospect Paid",
166
+ "Prospect Enrolled",
167
+ "Customer",
168
+ ],
169
+ },
170
+ history: {
171
+ type: Array as PropType<
172
+ Array<{ lifecycleStage?: string; updatedAt?: string }>
173
+ >,
174
+ default: () => [],
175
+ },
176
+ });
67
177
 
68
- const stepStatusEnum = ["contact" , "MQL" , 'SQL' , 'opportunity' , 'Prospect' , 'Prospect Paid' , 'Prospect Enrolled' , 'Customer']
178
+ const stepStatusEnum = computed(() => props.steps);
69
179
 
70
- const lifeCycleEntries = computed(() => {
71
- return Object.entries(stepStatusEnum).map((x) => {
72
- return { key: x[0], value: x[1] }
73
- })
74
- })
180
+ const lifeCycleEntries = computed(() => {
181
+ return Object.entries(stepStatusEnum.value).map((x) => {
182
+ return { key: x[0], value: x[1] };
183
+ });
184
+ });
75
185
 
76
- const lifeCycleState = computed(() => {
77
- return currentStatus.value
78
- })
186
+ const lifeCycleState = computed(() => {
187
+ return currentStatus.value;
188
+ });
79
189
 
80
- const currentStatus = computed(() => props.stepStatus)
190
+ const currentStatus = computed(() => props.stepStatus);
81
191
 
82
- const status = computed(() => lifeCycleState.value)
192
+ const status = computed(() => lifeCycleState.value);
83
193
 
84
- const lifeCycle = computed(() => {
85
- return lifeCycleEntries.value.findIndex((e) => e.value === status.value)
86
- })
194
+ const lifeCycle = computed(() => {
195
+ return lifeCycleEntries.value.findIndex((e) => e.value === status.value);
196
+ });
87
197
 
88
- const getStatus = computed(() => {
89
- return (status: string, index: number) => {
90
- if (
91
- index &&
92
- lifeCycle.value === lifeCycleEntries.value.length - 1
93
- ) {
94
- return 'complete'
95
- }
96
- if (index === lifeCycle.value) {
97
- return 'current'
98
- }
99
- if (index < lifeCycle.value) {
100
- return 'complete'
101
- }
102
- if (index > lifeCycle.value) {
103
- return 'upcoming'
104
- }
105
- return {
106
- status,
107
- }
198
+ const getStatus = computed(() => {
199
+ return (status: string, index: number) => {
200
+ if (index && lifeCycle.value === lifeCycleEntries.value.length - 1) {
201
+ return "complete";
202
+ }
203
+ if (index === lifeCycle.value) {
204
+ return "current";
205
+ }
206
+ if (index < lifeCycle.value) {
207
+ return "complete";
108
208
  }
109
- })
209
+ if (index > lifeCycle.value) {
210
+ return "upcoming";
211
+ }
212
+ return {
213
+ status,
214
+ };
215
+ };
216
+ });
217
+
218
+ function normalizeStage(value?: string) {
219
+ if (!value) return undefined;
220
+ const v = value.toUpperCase();
221
+ switch (v) {
222
+ case "CONTACT":
223
+ return "contact";
224
+ case "MQL":
225
+ return "MQL";
226
+ case "SAL":
227
+ return "SAL";
228
+ case "SQL":
229
+ return "SQL";
230
+ case "OPPORTUNITY":
231
+ return "opportunity";
232
+ case "PROSPECT":
233
+ return "Prospect";
234
+ case "PROSPECT PAID":
235
+ return "Prospect Paid";
236
+ case "PROSPECT ENROLLED":
237
+ return "Prospect Enrolled";
238
+ case "CUSTOMER":
239
+ return "Customer";
240
+ default:
241
+ return undefined;
242
+ }
243
+ }
244
+
245
+ function getFormattedDate(stepValue: string) {
246
+ const match = props.history.find(
247
+ (h) => normalizeStage(h.lifecycleStage) === stepValue
248
+ );
249
+ if (!match?.updatedAt) return "";
250
+ try {
251
+ const date = new Date(match.updatedAt);
252
+ return (
253
+ date.toLocaleDateString("en-GB", {
254
+ day: "numeric",
255
+ month: "short",
256
+ year: "numeric",
257
+ }) +
258
+ " at " +
259
+ date.toLocaleTimeString("en-US", {
260
+ hour: "numeric",
261
+ minute: "2-digit",
262
+ hour12: true,
263
+ })
264
+ );
265
+ } catch {
266
+ return "";
267
+ }
268
+ }
269
+
270
+ function getUpdatedByEmail(stepValue: string) {
271
+ const match = props.history.find(
272
+ (h) => normalizeStage(h.lifecycleStage) === stepValue
273
+ );
274
+ const email = (match as any)?.updatedBy?.email as string | undefined;
275
+ return email ? `Updatead by ${email}` : "";
276
+ }
110
277
  </script>
111
- <style lang="scss">
112
- </style>
278
+ <style lang="scss"></style>
@@ -9,7 +9,7 @@
9
9
 
10
10
  <button
11
11
  type="button"
12
- :class="[disabled ? 'pointer-events-none cursor-not-allowed bg-gray-50' : 'bg-white', inputFilled ? 'h-14 rounded-2xl' : 'h-10 rounded-md', 'relative w-full mb-2 border border-gray-100 cursor-pointer z-1 group focus-within:border-purple-600 focus-within:ring-1 focus-within:ring-purple-600']"
12
+ :class="[disabled ? 'pointer-events-none cursor-not-allowed bg-gray-50' : 'bg-white', inputFilled ? 'h-14 rounded-2xl' : 'h-10 rounded-md', 'relative w-full mb-2 border border-gray-200 cursor-pointer z-1 group focus-within:border-purple-600 focus-within:ring-1 focus-within:ring-purple-600']"
13
13
  @click="focusInput"
14
14
  >
15
15
  <label
@@ -46,7 +46,7 @@
46
46
  </label>
47
47
  <textarea
48
48
  ref="inputRef"
49
- :class="['min-h-[4.5rem] max-h-40 appearance-none block w-full px-4 py-2 placeholder:text-gray-400 text-sm font-medium relative z-0 bg-transparent border-gray-100 focus:outline-none focus:shadow-none focus-within:outline-none focus:border-purple-600 disabled:opacity-75 autofill:bg-white leading-6 placeholder:capitalize rounded-md border-solid border focus-within:ring-1 focus-within:ring-purple-600 transition-colors duration-300 ease-in-out',{'border-red-500': errors}]"
49
+ :class="['min-h-[4.5rem] max-h-40 appearance-none block w-full px-4 py-2 placeholder:text-gray-400 text-sm font-medium relative z-0 bg-transparent border-gray-200 focus:outline-none focus:shadow-none focus-within:outline-none focus:border-purple-600 disabled:opacity-75 autofill:bg-white leading-6 placeholder:capitalize rounded-md border-solid border focus-within:ring-1 focus-within:ring-purple-600 transition-colors duration-300 ease-in-out',{'border-red-500': errors}]"
50
50
  v-model="localValue"
51
51
  :placeholder="placeholder"
52
52
  :disabled="disabled"