From 60e828d47c232d2fe80d0d032515daf4cb991245 Mon Sep 17 00:00:00 2001 From: "Ng Wing Tat, David" Date: Wed, 11 Sep 2024 14:49:33 +0800 Subject: [PATCH 01/18] =?UTF-8?q?=F0=9F=8E=A8=20Rename=20error=20ref=20in?= =?UTF-8?q?=20affiliation=20link?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pages/affiliation-link.vue | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/pages/affiliation-link.vue b/pages/affiliation-link.vue index 83795ebc4..ddfce90f6 100644 --- a/pages/affiliation-link.vue +++ b/pages/affiliation-link.vue @@ -7,7 +7,7 @@ { + productIdError.value = '' +}) const isLoadingProductData = ref(false) const productData = ref(undefined) @@ -385,19 +388,19 @@ async function fetchProductData () { } else { const { data: classData, error: classFetchError } = await useFetch(`${LIKE_CO_API}/likernft/book/store/${productId.value}`) if (classFetchError.value) { - error.value = 'Cannot fetch class data.' + productIdError.value = 'Cannot fetch class data.' } else { return classData.value } } } catch { - error.value = 'Cannot fetch product data.' + productIdError.value = 'Cannot fetch product data.' } return undefined } async function createAffiliationLink () { - error.value = '' + productIdError.value = '' productData.value = undefined if (!productId.value) { From ceecc9c45b2641cc0e4bd6611fbbe66aa2abeca0 Mon Sep 17 00:00:00 2001 From: "Ng Wing Tat, David" Date: Wed, 11 Sep 2024 15:06:09 +0800 Subject: [PATCH 02/18] =?UTF-8?q?=F0=9F=A5=85=20Guard=20undefined=20produc?= =?UTF-8?q?t=20name?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pages/affiliation-link.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/affiliation-link.vue b/pages/affiliation-link.vue index ddfce90f6..1e7e35e92 100644 --- a/pages/affiliation-link.vue +++ b/pages/affiliation-link.vue @@ -319,7 +319,7 @@ const priceIndexOptions = computed(() => { })) }) -const tableTitle = computed(() => `${productName.value} Affiliation Links`) +const tableTitle = computed(() => `${productName.value ? `${productName.value} ` : ''}Affiliation Links`) const tableColumns = [ { key: 'channel', From 9327b0473dc795f644dda88661f7ed7a66e7fec3 Mon Sep 17 00:00:00 2001 From: "Ng Wing Tat, David" Date: Wed, 11 Sep 2024 16:11:32 +0800 Subject: [PATCH 03/18] =?UTF-8?q?=F0=9F=8E=A8=20Rename=20queryDefault=20to?= =?UTF-8?q?=20linkQueryDefault?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pages/affiliation-link.vue | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pages/affiliation-link.vue b/pages/affiliation-link.vue index 1e7e35e92..cc1eb4dbd 100644 --- a/pages/affiliation-link.vue +++ b/pages/affiliation-link.vue @@ -233,7 +233,7 @@ const productId = computed(() => { }) const linkQueryInput = ref('') -const defaultQuery = computed(() => { +const linkQueryDefault = computed(() => { const isUseLikerLandLink = linkSetting.value === 'liker_land' let utmSource = isUseLikerLandLink ? 'likerland' : 'stripe' if (isCustomLink.value) { @@ -246,13 +246,13 @@ const defaultQuery = computed(() => { }) const linkQuery = computed(() => { if (linkQueryInput.value) { - return { ...defaultQuery.value, ...Object.fromEntries(new URLSearchParams(linkQueryInput.value.trim())) } + return { ...linkQueryDefault.value, ...Object.fromEntries(new URLSearchParams(linkQueryInput.value.trim())) } } const input = productIdInput.value?.trim() || '' if (input.startsWith('http')) { - return { ...defaultQuery.value, ...Object.fromEntries(new URL(input).searchParams) } + return { ...linkQueryDefault.value, ...Object.fromEntries(new URL(input).searchParams) } } - return defaultQuery.value + return linkQueryDefault.value }) const linkQueryTableRows = computed(() => Object.entries(linkQuery.value).map(([key, value]) => ({ key, @@ -342,8 +342,8 @@ const tableRows = computed(() => { utm_campaign: `${channel.id}_bookpress` } const qrUtmSourceQuery: Record = {} - if (linkQuery.value.utm_source === defaultQuery.value.utm_source) { - qrUtmSourceQuery.utm_source = `${defaultQuery.value.utm_source}-qr` + if (linkQuery.value.utm_source === linkQueryDefault.value.utm_source) { + qrUtmSourceQuery.utm_source = `${linkQueryDefault.value.utm_source}-qr` } const urlConfig = { [isCollection.value ? 'collectionId' : 'classId']: productId.value, From d8cae2fafd365728f60f15b9dcb138646714b586 Mon Sep 17 00:00:00 2001 From: "Ng Wing Tat, David" Date: Wed, 11 Sep 2024 16:51:21 +0800 Subject: [PATCH 04/18] =?UTF-8?q?=F0=9F=8E=A8=20Update=20link=20query=20pr?= =?UTF-8?q?ecedence?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pages/affiliation-link.vue | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/pages/affiliation-link.vue b/pages/affiliation-link.vue index cc1eb4dbd..d83d926df 100644 --- a/pages/affiliation-link.vue +++ b/pages/affiliation-link.vue @@ -245,14 +245,18 @@ const linkQueryDefault = computed(() => { } }) const linkQuery = computed(() => { - if (linkQueryInput.value) { - return { ...linkQueryDefault.value, ...Object.fromEntries(new URLSearchParams(linkQueryInput.value.trim())) } - } + const mergedQuery = { ...linkQueryDefault.value } + const input = productIdInput.value?.trim() || '' if (input.startsWith('http')) { - return { ...linkQueryDefault.value, ...Object.fromEntries(new URL(input).searchParams) } + Object.assign(mergedQuery, Object.fromEntries(new URL(input).searchParams)) } - return linkQueryDefault.value + + if (linkQueryInput.value) { + Object.assign(mergedQuery, Object.fromEntries(new URLSearchParams(linkQueryInput.value))) + } + + return mergedQuery }) const linkQueryTableRows = computed(() => Object.entries(linkQuery.value).map(([key, value]) => ({ key, From 40644fce9199ed6622270ee8ffd68ed6b707e73c Mon Sep 17 00:00:00 2001 From: "Ng Wing Tat, David" Date: Wed, 11 Sep 2024 17:51:58 +0800 Subject: [PATCH 05/18] =?UTF-8?q?=F0=9F=90=9B=20Rewrite=20affiliation=20li?= =?UTF-8?q?nk=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- constant/index.ts | 4 +++- pages/affiliation-link.vue | 38 ++++++++++++++++++++++---------------- utils/index.ts | 17 +++++++++++------ 3 files changed, 36 insertions(+), 23 deletions(-) diff --git a/constant/index.ts b/constant/index.ts index 8f6683701..2db4f5d62 100644 --- a/constant/index.ts +++ b/constant/index.ts @@ -9,8 +9,10 @@ export const NFT_DEFAULT_MINT_AMOUNT = 50 export const LIKE_ADDRESS_REGEX = /^like1[ac-hj-np-z02-9]{38}$/ +export const AFFILIATION_CHANNEL_DEFAULT = 'liker_land' + export const AFFILIATION_CHANNELS = [ - { id: 'liker_land', name: 'Liker Land' }, + { id: AFFILIATION_CHANNEL_DEFAULT, name: 'Liker Land' }, { id: '@bookpunch', name: '一拳書店' }, { id: '@boundarybooks', name: '界限書店' }, { id: '@breakthrough_publish', name: '突破書廊' }, diff --git a/pages/affiliation-link.vue b/pages/affiliation-link.vue index d83d926df..5e2062a2d 100644 --- a/pages/affiliation-link.vue +++ b/pages/affiliation-link.vue @@ -134,9 +134,10 @@ :ui="{ body: { padding: '' } }" > import { type FileExtension } from '@likecoin/qr-code-styling' -import { AFFILIATION_CHANNELS } from '~/constant' + +import { AFFILIATION_CHANNEL_DEFAULT, AFFILIATION_CHANNELS } from '~/constant' + import { useCollectionStore } from '~/stores/collection' import { getPurchaseLink } from '~/utils' @@ -330,6 +333,10 @@ const tableColumns = [ label: 'Channel', sortable: true }, + { + key: 'utmCampaign', + label: 'UTM Campaign' + }, { key: 'link', label: 'Link', @@ -342,30 +349,29 @@ const tableRows = computed(() => { } const channels = [...customChannels.value, ...AFFILIATION_CHANNELS] return channels.map((channel) => { - const utmCampaignQuery = { - utm_campaign: `${channel.id}_bookpress` - } - const qrUtmSourceQuery: Record = {} - if (linkQuery.value.utm_source === linkQueryDefault.value.utm_source) { - qrUtmSourceQuery.utm_source = `${linkQueryDefault.value.utm_source}-qr` + let utmCampaign = 'bookpress' + if (channel.id !== AFFILIATION_CHANNEL_DEFAULT) { + utmCampaign = `${convertChannelIdToLikerId(channel.id)}_${utmCampaign}` } const urlConfig = { [isCollection.value ? 'collectionId' : 'classId']: productId.value, channel: channel.id, priceIndex: priceIndex.value, customLink: isCustomLink.value ? customLinkInput.value : undefined, - isUseLikerLandLink: linkSetting.value === 'liker_land' + isUseLikerLandLink: linkSetting.value === 'liker_land', + query: { + utm_campaign: utmCampaign, + ...linkQuery.value + } } return { id: channel.id, channel: channel.name, - url: getPurchaseLink({ - ...urlConfig, - query: { ...utmCampaignQuery, ...linkQuery.value } - }), + utmCampaign, + url: getPurchaseLink(urlConfig), qrCodeUrl: getPurchaseLink({ ...urlConfig, - query: { ...utmCampaignQuery, ...linkQuery.value, ...qrUtmSourceQuery } + isForQRCode: linkQuery.value.utm_source === linkQueryDefault.value.utm_source }) } }) diff --git a/utils/index.ts b/utils/index.ts index 6af315dda..7274acf8a 100644 --- a/utils/index.ts +++ b/utils/index.ts @@ -117,7 +117,8 @@ export function getPurchaseLink ({ coupon, customLink, isUseLikerLandLink, - query + isForQRCode, + query: queryInput }:{ classId?: string collectionId?: string @@ -126,25 +127,29 @@ export function getPurchaseLink ({ coupon?: string customLink?: string isUseLikerLandLink?: boolean + isForQRCode?: boolean query?: Record }) { - const payload: Record = { + const query: Record = { from: channel || '' } if (classId) { - payload.price_index = priceIndex.toString() + query.price_index = priceIndex.toString() } - if (coupon) { payload.coupon = coupon } + if (coupon) { query.coupon = coupon } if (customLink) { const url = new URL(customLink) - Object.entries(payload).forEach(([key, value]) => { + Object.entries(query).forEach(([key, value]) => { url.searchParams.set(key, value) }) return url.toString() } + if (isForQRCode && queryInput?.utm_medium) { + query.utm_medium = `${queryInput.utm_medium}-qr` + } const { LIKE_CO_API, LIKER_LAND_URL } = useRuntimeConfig().public - const queryString = `?${new URLSearchParams({ ...query, ...payload }).toString()}` + const queryString = `?${new URLSearchParams({ ...queryInput, ...query }).toString()}` if (collectionId) { return isUseLikerLandLink ? `${LIKER_LAND_URL}/nft/collection/${collectionId}${queryString}` From 980d87c50d03a741153d0e4965b073fe7f5b9222 Mon Sep 17 00:00:00 2001 From: "Ng Wing Tat, David" Date: Wed, 11 Sep 2024 18:17:59 +0800 Subject: [PATCH 06/18] =?UTF-8?q?=F0=9F=92=AC=20Update=20field=20label=20i?= =?UTF-8?q?n=20affiliation=20link?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pages/affiliation-link.vue | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pages/affiliation-link.vue b/pages/affiliation-link.vue index 5e2062a2d..4d2bc1d78 100644 --- a/pages/affiliation-link.vue +++ b/pages/affiliation-link.vue @@ -17,13 +17,13 @@ /> - + productId.value?.startsWith('col_')) const linkSettings = ref([ { - name: 'Use Liker Land Link (Product page)', + name: 'Liker Land Product Page', value: 'liker_land' }, { - name: 'Direct Link (Stripe)', + name: 'Stripe Checkout Page', value: 'direct' }, { - name: 'Custom Link', + name: 'Custom Page', value: 'custom' } ]) From 9e483d5e4d6a3c4476179843785ca589622ef1a6 Mon Sep 17 00:00:00 2001 From: "Ng Wing Tat, David" Date: Fri, 13 Sep 2024 11:11:21 +0800 Subject: [PATCH 07/18] =?UTF-8?q?=F0=9F=A5=85=20Guard=20custom=20channel?= =?UTF-8?q?=20input?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pages/affiliation-link.vue | 74 +++++++++++++++++++++++++++++++------- stores/static-data.ts | 50 ++++++++++++++++++++++++++ utils/api.ts | 18 ++++++++++ utils/index.ts | 8 +++++ 4 files changed, 137 insertions(+), 13 deletions(-) create mode 100644 stores/static-data.ts create mode 100644 utils/api.ts diff --git a/pages/affiliation-link.vue b/pages/affiliation-link.vue index 4d2bc1d78..8629b73ea 100644 --- a/pages/affiliation-link.vue +++ b/pages/affiliation-link.vue @@ -13,6 +13,7 @@ v-model="productIdInput" class="font-mono" placeholder="https://liker.land/nft/class/likenft1ku4ra0e7dgknhd0wckrkxspuultynl4mgkxa3j08xeshfr2l0ujqmmvy83" + name="product_id" :required="true" /> @@ -30,15 +31,21 @@ v-model="customLinkInput" class="font-mono" placeholder="https://books.liker.land" + name="custom_link" :required="true" /> - + @@ -47,6 +54,7 @@ v-model="linkQueryInput" class="font-mono" placeholder="utm_source=instagram&utm_medium=social" + name="query_params" /> @@ -152,6 +160,13 @@ + + - - + + @@ -58,7 +58,7 @@

You have not setup your channel ID yet.

- - +