-
Hello from Vue
-
{{ message }}
+
-
diff --git a/assets/vue/api.js b/assets/vue/api.js
new file mode 100644
index 0000000..7cc9e77
--- /dev/null
+++ b/assets/vue/api.js
@@ -0,0 +1,58 @@
+import {
+ CampaignClient,
+ Client,
+ ListMessagesClient,
+ ListClient,
+ StatisticsClient,
+ SubscribersClient,
+ SubscriptionClient,
+ SubscriberAttributesClient,
+ TemplatesClient
+} from '@tatevikgr/rest-api-client';
+
+const appElement = document.getElementById('vue-app');
+const apiToken = appElement?.dataset.apiToken;
+const apiBaseUrl = appElement?.dataset.apiBaseUrl;
+
+if (!apiBaseUrl) {
+ console.error('API Base URL is not configured.');
+}
+
+const client = new Client(apiBaseUrl || '');
+
+if (apiToken) {
+ client.setSessionId(apiToken);
+}
+
+export const subscribersClient = new SubscribersClient(client);
+export const listClient = new ListClient(client);
+export const campaignClient = new CampaignClient(client);
+export const listMessagesClient = new ListMessagesClient(client);
+export const statisticsClient = new StatisticsClient(client);
+export const subscriptionClient = new SubscriptionClient(client);
+export const subscriberAttributesClient = new SubscriberAttributesClient(client);
+export const templateClient = new TemplatesClient(client);
+
+export const fetchAllLists = async ({ limit = 100, maxPages = 100 } = {}) => {
+ const lists = [];
+ let afterId = null;
+
+ for (let pageIndex = 0; pageIndex < maxPages; pageIndex += 1) {
+ const response = await listClient.getLists(afterId, limit);
+ const items = Array.isArray(response?.items) ? response.items : [];
+ lists.push(...items);
+
+ const hasMore = response?.pagination?.hasMore === true;
+ const nextCursor = response?.pagination?.nextCursor;
+
+ if (!hasMore || !Number.isFinite(nextCursor) || nextCursor === afterId) {
+ break;
+ }
+
+ afterId = nextCursor;
+ }
+
+ return lists;
+};
+
+export default client;
diff --git a/assets/vue/components/base/BaseBadge.spec.js b/assets/vue/components/base/BaseBadge.spec.js
new file mode 100644
index 0000000..df0507a
--- /dev/null
+++ b/assets/vue/components/base/BaseBadge.spec.js
@@ -0,0 +1,33 @@
+import { mount } from '@vue/test-utils'
+import BaseBadge from './BaseBadge.vue'
+
+describe('BaseBadge', () => {
+ it('renders neutral variant by default', () => {
+ const wrapper = mount(BaseBadge, {
+ slots: {
+ default: 'All',
+ },
+ })
+
+ const classes = wrapper.get('span').classes()
+ expect(wrapper.text()).toContain('All')
+ expect(classes).toContain('bg-gray-100')
+ expect(classes).toContain('text-gray-800')
+ })
+
+ it('renders counter variant styles', () => {
+ const wrapper = mount(BaseBadge, {
+ props: {
+ variant: 'counter',
+ },
+ slots: {
+ default: '10',
+ },
+ })
+
+ const classes = wrapper.get('span').classes()
+ expect(classes).toContain('bg-indigo-50')
+ expect(classes).toContain('text-ext-wf3')
+ expect(wrapper.text()).toContain('10')
+ })
+})
diff --git a/assets/vue/components/base/BaseBadge.vue b/assets/vue/components/base/BaseBadge.vue
new file mode 100644
index 0000000..217fd1b
--- /dev/null
+++ b/assets/vue/components/base/BaseBadge.vue
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
diff --git a/assets/vue/components/base/BaseButton.spec.js b/assets/vue/components/base/BaseButton.spec.js
new file mode 100644
index 0000000..5a0106b
--- /dev/null
+++ b/assets/vue/components/base/BaseButton.spec.js
@@ -0,0 +1,47 @@
+import { mount } from '@vue/test-utils'
+import BaseButton from './BaseButton.vue'
+
+describe('BaseButton', () => {
+ it('renders slot content', () => {
+ const wrapper = mount(BaseButton, {
+ slots: {
+ default: 'Save',
+ },
+ })
+
+ expect(wrapper.text()).toContain('Save')
+ })
+
+ it('uses primary styles by default', () => {
+ const wrapper = mount(BaseButton)
+ const classes = wrapper.get('button').classes()
+
+ expect(classes).toContain('text-white')
+ expect(classes).toContain('bg-blue-600')
+ })
+
+ it('uses secondary styles when variant is secondary', () => {
+ const wrapper = mount(BaseButton, {
+ props: {
+ variant: 'secondary',
+ },
+ })
+
+ const classes = wrapper.get('button').classes()
+ expect(classes).toContain('text-gray-700')
+ expect(classes).toContain('bg-white')
+ })
+
+ it('forwards attributes to button', () => {
+ const wrapper = mount(BaseButton, {
+ attrs: {
+ disabled: true,
+ 'data-testid': 'submit-button',
+ },
+ })
+
+ const button = wrapper.get('button')
+ expect(button.attributes('disabled')).toBeDefined()
+ expect(button.attributes('data-testid')).toBe('submit-button')
+ })
+})
diff --git a/assets/vue/components/base/BaseButton.vue b/assets/vue/components/base/BaseButton.vue
new file mode 100644
index 0000000..aaeb2e8
--- /dev/null
+++ b/assets/vue/components/base/BaseButton.vue
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
diff --git a/assets/vue/components/base/BaseCard.vue b/assets/vue/components/base/BaseCard.vue
new file mode 100644
index 0000000..2278c74
--- /dev/null
+++ b/assets/vue/components/base/BaseCard.vue
@@ -0,0 +1,34 @@
+
+
+
+
+
+
diff --git a/assets/vue/components/base/BaseIcon.spec.js b/assets/vue/components/base/BaseIcon.spec.js
new file mode 100644
index 0000000..047d803
--- /dev/null
+++ b/assets/vue/components/base/BaseIcon.spec.js
@@ -0,0 +1,36 @@
+import { mount } from '@vue/test-utils'
+import BaseIcon from './BaseIcon.vue'
+
+describe('BaseIcon', () => {
+ it('renders icon svg for known icon name', () => {
+ const wrapper = mount(BaseIcon, {
+ props: {
+ name: 'users',
+ },
+ })
+
+ expect(wrapper.html()).toContain('