From 1a6eb7e2ff0bf551e1560c67170e90fa8ec2a8ea Mon Sep 17 00:00:00 2001 From: Joe Carstairs <118172583+jcarstairs-scottlogic@users.noreply.github.com> Date: Thu, 25 Jan 2024 16:59:42 +0000 Subject: 33 / Pay annual subscription by PayPal (#68) * Refactors paypal-buttons -> paypal-product-buttons WebComponent * Pulls out address to data file * Defines annual subscription prices data hash * Styles paypal-product-buttons * Styles
* Details have cursor:pointer on the summary * Moves Jyne section to separate page * Rewrites payment options using details components * Reworks translation typing * Defines MembershipType enum * Adds translations for MembershipType enum * Can pay annual subs by PayPal * Styles tables better --- scss/base/_address.scss | 5 + scss/base/_details.scss | 4 + scss/base/_index.scss | 5 + scss/base/_paypal-product-buttons.scss | 4 + scss/base/_table.scss | 23 +++ scss/modules/_index.scss | 1 + scss/modules/_payment-methods-list.scss | 8 + scss/values/spacing/_constants.scss | 2 +- src/components/LallansIssue.astro | 4 +- src/components/Navbar/NavLinks.astro | 3 +- src/components/PayPalButtons.astro | 150 ---------------- src/components/PaypalProductButtons.astro | 26 +++ src/components/Scotsoun.astro | 4 +- src/data/contactDetails.ts | 16 ++ src/data/subscriptionPrices.ts | 7 + src/enums/MembershipType.ts | 42 +++++ src/i18n/translate.ts | 28 +-- src/i18n/translations/components/breadcrumbs.ts | 20 +++ src/i18n/translations/enums/membershipType.ts | 21 +++ src/i18n/translations/pages/home.ts | 76 --------- src/i18n/translations/pages/jyne.ts | 190 +++++++++++++++++++++ .../furthsettins/lallans/[issueNumber]/index.astro | 1 + .../furthsettins/scotsoun/[scotsounId]/index.astro | 1 + src/pages/[locale]/index.astro | 27 +-- src/pages/[locale]/jyne/index.astro | 104 +++++++++++ .../jyne/paypal/[membershipType]/index.astro | 41 +++++ .../jyne/paypal/[membershipType]/success.astro | 30 ++++ src/pages/[locale]/jyne/paypal/index.astro | 27 +++ src/scripts/wc/paypal-product-buttons.ts | 146 ++++++++++++++++ src/types/TranslationsDictionary.d.ts | 15 +- tsconfig.json | 1 + 31 files changed, 755 insertions(+), 277 deletions(-) create mode 100644 scss/base/_address.scss create mode 100644 scss/base/_details.scss create mode 100644 scss/base/_paypal-product-buttons.scss create mode 100644 scss/base/_table.scss create mode 100644 scss/modules/_payment-methods-list.scss delete mode 100644 src/components/PayPalButtons.astro create mode 100644 src/components/PaypalProductButtons.astro create mode 100644 src/data/subscriptionPrices.ts create mode 100644 src/enums/MembershipType.ts create mode 100644 src/i18n/translations/enums/membershipType.ts create mode 100644 src/i18n/translations/pages/jyne.ts create mode 100644 src/pages/[locale]/jyne/index.astro create mode 100644 src/pages/[locale]/jyne/paypal/[membershipType]/index.astro create mode 100644 src/pages/[locale]/jyne/paypal/[membershipType]/success.astro create mode 100644 src/pages/[locale]/jyne/paypal/index.astro create mode 100644 src/scripts/wc/paypal-product-buttons.ts diff --git a/scss/base/_address.scss b/scss/base/_address.scss new file mode 100644 index 0000000..0206b83 --- /dev/null +++ b/scss/base/_address.scss @@ -0,0 +1,5 @@ +address { + margin-block: var(--spacing-block-md); + font-style: normal; + margin-inline-start: var(--spacing-inline-lg); +} diff --git a/scss/base/_details.scss b/scss/base/_details.scss new file mode 100644 index 0000000..6d0b1d4 --- /dev/null +++ b/scss/base/_details.scss @@ -0,0 +1,4 @@ +details summary { + cursor: pointer; +} + diff --git a/scss/base/_index.scss b/scss/base/_index.scss index f338772..527b4c7 100644 --- a/scss/base/_index.scss +++ b/scss/base/_index.scss @@ -1,4 +1,9 @@ +@use 'address'; +@use 'details'; @use 'dl'; @use 'list'; +@use 'paypal-product-buttons'; +@use 'table'; @use 'theme'; @use 'typography'; + diff --git a/scss/base/_paypal-product-buttons.scss b/scss/base/_paypal-product-buttons.scss new file mode 100644 index 0000000..7339ede --- /dev/null +++ b/scss/base/_paypal-product-buttons.scss @@ -0,0 +1,4 @@ +paypal-product-buttons div { + margin-block-start: var(--spacing-block-md); +} + diff --git a/scss/base/_table.scss b/scss/base/_table.scss new file mode 100644 index 0000000..ca4f957 --- /dev/null +++ b/scss/base/_table.scss @@ -0,0 +1,23 @@ +table { + border-collapse: collapse; + background-color: var(--background-color); // side-scrolling may be needed on narrow screens + + caption { + font-style: italic; + font-size: var(--font-size-0); + } +} + +thead, th { + border-block-end: var(--border-width) solid var(--text-color); +} + +:is(th, td) { + padding-inline: var(--spacing-inline-sm); + padding-block: var(--spacing-block-sm); + + + :is(th, td) { + border-inline-start: calc(var(--border-width) / 2) solid var(--text-color); + } +} + diff --git a/scss/modules/_index.scss b/scss/modules/_index.scss index 6e67241..822295f 100644 --- a/scss/modules/_index.scss +++ b/scss/modules/_index.scss @@ -5,6 +5,7 @@ @use 'link-gallery' as *; @use 'nav' as *; @use 'news-index' as *; +@use 'payment-methods-list' as *; @use 'product' as *; @use 'schedule' as *; @use 'skip-to-content' as *; diff --git a/scss/modules/_payment-methods-list.scss b/scss/modules/_payment-methods-list.scss new file mode 100644 index 0000000..a59831a --- /dev/null +++ b/scss/modules/_payment-methods-list.scss @@ -0,0 +1,8 @@ +.payment-methods-list { + list-style: none; + + details[open] :last-child { + margin-block-end: var(--spacing-block-sm); + } +} + diff --git a/scss/values/spacing/_constants.scss b/scss/values/spacing/_constants.scss index 9a990b8..c89fdc8 100644 --- a/scss/values/spacing/_constants.scss +++ b/scss/values/spacing/_constants.scss @@ -42,7 +42,7 @@ $grid-padding-er: ( ); $spacing-factor: ( - 'inline': (lg: 4, md: 2, sm: 1), + 'inline': (lg: 12, md: 6, sm: 3), 'block': (lg: 16, md: 10, sm: 5) ); diff --git a/src/components/LallansIssue.astro b/src/components/LallansIssue.astro index c8bc6eb..317ff2d 100644 --- a/src/components/LallansIssue.astro +++ b/src/components/LallansIssue.astro @@ -7,7 +7,7 @@ import type LallansIssue from '$types/LallansIssue'; import { Image } from 'astro:assets'; import LallansIssueContributions from './LallansIssueContributions.astro'; import LallansIssueContributors from './LallansIssueContributors.astro'; -import PayPalButtons from './PayPalButtons.astro'; +import PaypalProductButtons from './PaypalProductButtons.astro'; interface Props { issue: LallansIssue; @@ -27,7 +27,7 @@ const t = translate(locale);
- {t(tFurthsettins, { key: 'title' })}
  • - {t(tHame, { key: 'jyne' })} + {t(tJyne, { key: 'title' })}
  • {t(tHame, { key: 'contact' })} diff --git a/src/components/PayPalButtons.astro b/src/components/PayPalButtons.astro deleted file mode 100644 index cdea5cb..0000000 --- a/src/components/PayPalButtons.astro +++ /dev/null @@ -1,150 +0,0 @@ ---- -import env from '$lib/env'; -import type Price from '$types/Price'; - -interface Props { - successPageUrl: string; - productDescription: string; - shortDescription: string; - totalPrice: Price; -} - -const { successPageUrl, productDescription, shortDescription, totalPrice } = Astro.props; - -const PAYPAL_CLIENT_ID = - env.ENVIRONMENT === 'prod' ? env.PAYPAL_LIVE_CLIENT_ID : env.PAYPAL_SANDBOX_CLIENT_ID; ---- - - -
    -
    - - diff --git a/src/components/PaypalProductButtons.astro b/src/components/PaypalProductButtons.astro new file mode 100644 index 0000000..0ebbd3f --- /dev/null +++ b/src/components/PaypalProductButtons.astro @@ -0,0 +1,26 @@ +--- +import env from '$lib/env'; +import type Price from '$types/Price'; + +interface Props { + successPageUrl: string; + productDescription: string; + shortDescription: string; + totalPrice: Price; +} + +const { successPageUrl, productDescription, shortDescription, totalPrice } = Astro.props; + +const PAYPAL_CLIENT_ID = + env.ENVIRONMENT === 'prod' ? env.PAYPAL_LIVE_CLIENT_ID : env.PAYPAL_SANDBOX_CLIENT_ID; +--- + + + Loading PayPal buttons… + diff --git a/src/components/Scotsoun.astro b/src/components/Scotsoun.astro index cca330d..7acae8b 100644 --- a/src/components/Scotsoun.astro +++ b/src/components/Scotsoun.astro @@ -4,7 +4,7 @@ import translate from '$i18n/translate'; import tSite from '$i18n/translations/site'; import tComponent from '$i18n/translations/components/scotsoun'; import type Scotsoun from '$types/Scotsoun'; -import PayPalButtons from './PayPalButtons.astro'; +import PaypalProductButtons from './PaypalProductButtons.astro'; import relativePath from '$lib/relativePath'; import { getLocaleFromPathOrThrow } from '$lib/getLocaleFromPath'; @@ -27,7 +27,7 @@ const t = translate(locale);
    - = TranslationsDictionary<{ - [key in Key]: { [locale in typeof defaultLocale]: Translation } & { - [locale in Locale]?: Translation; - }; + [key in Key]: LocalesTranslationHash; }>; export default function (locale: Locale) { - return function < - Key extends string, - Params, - Dict extends TranslationsDictionaryWith, - >(dict: Dict, params: { key: Key } & { [Param in keyof Params]: string }) { + return function ( + dict: TranslationsDictionaryWith, + params: { key: Key } & ParamsHash + ) { if (!(params.key in dict)) { throw new Error(` Could not find translation for key '${params.key}'. Available keys were: @@ -26,7 +26,9 @@ export default function (locale: Locale) { .join(', ')} `); } - const translation = dict[params.key][locale] ?? dict[params.key][defaultLocale]; + const localesTranslationHash: LocalesTranslationHash = dict[params.key]; + const translation: Translation = + localesTranslationHash[locale] ?? localesTranslationHash[defaultLocale]; return translation(params); }; } diff --git a/src/i18n/translations/components/breadcrumbs.ts b/src/i18n/translations/components/breadcrumbs.ts index 5aeff0e..8e3d027 100644 --- a/src/i18n/translations/components/breadcrumbs.ts +++ b/src/i18n/translations/components/breadcrumbs.ts @@ -22,6 +22,26 @@ const tBreadcrumbs = { sco: () => 'Scotsoun', 'en-GB': () => 'Scotsoun', }, + '/jyne/': { + sco: () => 'Jyne', + 'en-GB': () => 'Join', + }, + '/jyne/paypal/': { + sco: () => 'PayPal', + 'en-GB': () => 'PayPal', + }, + '/jyne/paypal/individual-uk/': { + sco: () => 'Individual (UK)', + 'en-GB': () => 'Individual (UK)', + }, + '/jyne/paypal/individual-non-uk/': { + sco: () => 'Individual (non-UK)', + 'en-GB': () => 'Individual (non-UK)', + }, + '/jyne/paypal/institution-uk/': { + sco: () => 'Institutional (UK)', + 'en-GB': () => 'Institutional (UK)', + }, '/news/': { sco: () => 'News', 'en-GB': () => 'News', diff --git a/src/i18n/translations/enums/membershipType.ts b/src/i18n/translations/enums/membershipType.ts new file mode 100644 index 0000000..0e1db06 --- /dev/null +++ b/src/i18n/translations/enums/membershipType.ts @@ -0,0 +1,21 @@ +import type { TranslationsDictionary, LocalesTranslationHash } from '$types/TranslationsDictionary'; + +import MembershipType from '$enums/MembershipType'; + +const tEnum: { [key in MembershipType]: LocalesTranslationHash } = { + [MembershipType.IndividualUk]: { + sco: () => 'Individual (UK)', + 'en-GB': () => 'Individual (UK)', + }, + [MembershipType.IndividualNonUk]: { + sco: () => 'Individual (non-UK)', + 'en-GB': () => 'Individual (non-UK)', + }, + [MembershipType.InstitutionUk]: { + sco: () => 'Institutional (UK)', + 'en-GB': () => 'Institutional (UK)', + }, +}; + +type Raw = typeof tEnum; +export default tEnum as TranslationsDictionary; diff --git a/src/i18n/translations/pages/home.ts b/src/i18n/translations/pages/home.ts index b577a46..6628682 100644 --- a/src/i18n/translations/pages/home.ts +++ b/src/i18n/translations/pages/home.ts @@ -119,82 +119,6 @@ const tPage = { sco: () => 'Aw furthsettins', 'en-GB': () => 'All publications', }, - jyne: { - sco: () => 'Jyne', - 'en-GB': () => 'Join', - }, - 'why-jyne': { - sco: () => 'Why jyne the Scots Leid Associe?', - 'en-GB': () => 'Why join the Scots Language Society?', - }, - 'why-jyne-para-1': { - sco: () => ` - Bi jynin us, ye ar helpin us tae heize up the Scots leid, maistly bi giein - Scots scrievers a place tae lairn frae anither an finnd their ain Scots vyce. - `, - 'en-GB': () => ` - By joining us, you are helping us to champion the Scots language, - especially by giving Scots writers a place to learn from one - another and to find their own Scots voice. - `, - }, - 'why-jyne-para-2': { - sco: () => ` - Forby, memmers get biannual issues o our aesome Lallans magazine gratis, - an hae the richt tae atten our Annual Collogues, whaur we haud our AGM an - enjoy talks frae kenspeckle spikkers on thochtie maiters effeirin til Scots - leid an leiteratur. - `, - 'en-GB': () => ` - In addition, members get biannual issues of our unique Lallans - magazine gratis, and have the right to attend our Annual Conferences, where - we hold our AGM and enjoy talks from highly regarded speakers on - thought-provoking subjects relating to Scots language and literature. - `, - }, - 'why-jyne-para-3': { - sco: () => ` - Gin whit ye ar seekin is a community o prose-scrievers, makars, pleywreichts - an owersetters, aw wi muckle care fur the Scots leid, the ar nae peer wi hus, - the auldest Scots-leid activist organisation that’s about eenou an the anely - fully dedicatit til the forderance o new Scots leiteratur. - `, - 'en-GB': () => ` - If what you’re looking for is a community of prose-writers, poets, - playwrights and translators with a passion for the Scots language, we have - no equal. We are the oldest extant Scots-language activist organisation and - the only one solely dedicated to the development of new Scots literature. - `, - }, - 'hou-jyne': { - sco: () => 'Hou can A jyne?', - 'en-GB': () => 'How can I join?', - }, - 'hou-jyne-para-1': { - sco: () => ` - Fur individuals, memmership costs £20 per annum. Fur institutions, the stent is £25 per annum. - `, - 'en-GB': () => ` - For individuals, membership costs £20 per annum. For institutions, the fee is £25 per annum. - `, - }, - 'hou-jyne-para-2': { - sco: ({ membershipEmail }: { membershipEmail: string }) => ` - Fur tae become a memmer, simply get in titch wi us at - ${membershipEmail} - an we’ll provide ye wi our bank details; or ye can jyne bi sendin us a cheque - made peyable til The Scots Language Society. See - Contact us - fur our address. - `, - 'en-GB': ({ membershipEmail }: { membershipEmail: string }) => ` - To become a member, simply get in touch with us at - ${membershipEmail} - and we’ll provide you with our bank details; or you can join by sending us - a cheque made payable to The Scots Language Society. See - Contact us for our address. - `, - }, contact: { sco: () => 'Contact', 'en-GB': () => 'Contact', diff --git a/src/i18n/translations/pages/jyne.ts b/src/i18n/translations/pages/jyne.ts new file mode 100644 index 0000000..bbcc67c --- /dev/null +++ b/src/i18n/translations/pages/jyne.ts @@ -0,0 +1,190 @@ +import type { TranslationsDictionary } from '$types/TranslationsDictionary'; + +import MembershipType from '$enums/MembershipType'; +import { annualSubscriptionPrice } from '$data/subscriptionPrices'; +import tMembershipType from '$i18n/translations/enums/membershipType'; +import translate from '$i18n/translate'; + +const tPage = { + title: { + sco: () => 'Jyne', + 'en-GB': () => 'Join', + }, + 'why-jyne': { + sco: () => 'Why jyne the Scots Leid Associe?', + 'en-GB': () => 'Why join the Scots Language Society?', + }, + 'why-jyne-para-1': { + sco: () => ` + Bi jynin us, ye ar helpin us tae heize up the Scots leid, maistly bi giein + Scots scrievers a place tae lairn frae anither an finnd their ain Scots vyce. + `, + 'en-GB': () => ` + By joining us, you are helping us to champion the Scots language, + especially by giving Scots writers a place to learn from one + another and to find their own Scots voice. + `, + }, + 'why-jyne-para-2': { + sco: () => ` + Forby, memmers get biannual issues o our aesome Lallans magazine gratis, + an hae the richt tae atten our Annual Collogues, whaur we haud our AGM an + enjoy talks frae kenspeckle spikkers on thochtie maiters effeirin til Scots + leid an leiteratur. + `, + 'en-GB': () => ` + In addition, members get biannual issues of our unique Lallans + magazine gratis, and have the right to attend our Annual Conferences, where + we hold our AGM and enjoy talks from highly regarded speakers on + thought-provoking subjects relating to Scots language and literature. + `, + }, + 'why-jyne-para-3': { + sco: () => ` + Gin whit ye ar seekin is a community o prose-scrievers, makars, pleywreichts + an owersetters, aw wi muckle care fur the Scots leid, the ar nae peer wi hus, + the auldest Scots-leid activist organisation that’s about eenou an the anely + fully dedicatit til the forderance o new Scots leiteratur. + `, + 'en-GB': () => ` + If what you’re looking for is a community of prose-writers, poets, + playwrights and translators with a passion for the Scots language, we have + no equal. We are the oldest extant Scots-language activist organisation and + the only one solely dedicated to the development of new Scots literature. + `, + }, + 'hou-jyne': { + sco: () => 'Hou can A jyne?', + 'en-GB': () => 'How can I join?', + }, + 'hou-jyne-intro': { + sco: () => ` + For tae jyne, ye can pey by PayPal, bank transfer or cheque. The fee is + as ablo. + `, + 'en-GB': () => ` + To join, you can pay by PayPal, bank transfer or cheque. The fee is as below. + `, + }, + 'subscription-price-table-caption': { + sco: () => 'Annual subscription price per membership type', + 'en-GB': () => 'Annual subscription price per membership type', + }, + 'hou-pey-by-paypal-title': { + sco: () => 'PayPal', + 'en-GB': () => 'PayPal', + }, + 'hou-pey-by-paypal-para': { + sco: () => 'Select a membership type', + 'en-GB': () => 'Select a membership type', + }, + 'membership-type-individual-uk': { + sco: () => 'Ane year individual UK membership', + 'en-GB': () => 'One year individual UK membership', + }, + 'membership-type-individual-uk-para': { + sco: () => ` + Here, ye can pey for ane year’s annual subscription til the Scots Leid Associe + gin ye ar an individual and ye want yer copies o Lallans sent til + a UK address. This will cost + £${annualSubscriptionPrice[MembershipType.IndividualUk]}. Gin ye want + some ither kind o membership, + see the ither options. + `, + 'en-GB': () => ` + Here, you can pay for one year’s annual subscription to the Scots Language + Society if you are an individual and you want your copies of Lallans + sent to a UK address. This will cost + £${annualSubscriptionPrice[MembershipType.IndividualNonUk]}. If you want + some other kind of membership, + see the other options. + `, + }, + 'membership-type-individual-non-uk': { + sco: () => 'Ane year individual non-UK membership', + 'en-GB': () => 'One year individual non-UK membership', + }, + 'membership-type-individual-non-uk-para': { + sco: () => ` + Here, ye can pey for ane year’s annual subscription til the Scots Leid Associe + gin ye ar an individual an ye want yer copies o Lallans sent til + a non-UK address. This will cost + £${annualSubscriptionPrice[MembershipType.IndividualNonUk]}. Gin ye want + some ither kind o membership, + see the ither options. + `, + 'en-GB': () => ` + Here, you can pay for one year’s annual subscription to the Scots Language + Society if you are an individual and you want your copies of Lallans + sent to a non-UK address. This will cost + £${annualSubscriptionPrice[MembershipType.IndividualNonUk]}. If you want + some other kind of membership, + see the other options. + `, + }, + 'membership-type-institution-uk': { + sco: () => 'Ane year UK institutional membership', + 'en-GB': () => 'One year UK institutional membership', + }, + 'membership-type-institution-uk-para': { + sco: () => ` + Here, ye can pey for ane year’s annual subscription til the Scots Leid Associe + gin ye ar an institution. This will cost + £${annualSubscriptionPrice[MembershipType.InstitutionUk]}. Gin ye want + some ither kind o membership, + see the ither options. + `, + 'en-GB': () => ` + Here, you can pay for one year’s annual subscription to the Scots + Language Society if you are an institution based in the UK. This will + cost £${annualSubscriptionPrice[MembershipType.InstitutionUk]}. If you + want some other kind of membership, + see the other options. + `, + }, + 'hou-pey-by-bank-transfer-title': { + sco: () => 'Bank transfer', + 'en-GB': () => 'Bank transfer', + }, + 'hou-pey-by-bank-transfer-para': { + sco: ({ emailAddress }: { emailAddress: string }) => ` + Send us an email at ${emailAddress} an we’ll send ye oor bank details. + `, + 'en-GB': ({ emailAddress }: { emailAddress: string }) => ` + Send us an email at ${emailAddress} and we’ll send you our bank details. + `, + }, + 'hou-pey-by-cheque-title': { + sco: () => 'Cheque', + 'en-GB': () => 'Cheque', + }, + 'hou-pey-by-cheque-para': { + sco: ({ address }: { address: string[] }) => ` + Mak out a cheque til The Scots Language Society, syne send it til us at the follaein address: +
    ${address.join('
    ')}
    + `, + 'en-GB': ({ address }: { address: string[] }) => ` + Make out a cheque to The Scots Language Society, then send it to us at the following address: +
    ${address.join('
    ')}
    + `, + }, + success: { + sco: () => 'Successfullie bocht membership', + 'en-GB': () => 'Successfully bought membership', + }, + 'success-para': { + sco: ({ membershipType }: { membershipType: MembershipType }) => ` + Ye hae successfullie bocht ane year’s + ${translate('sco')(tMembershipType, { key: membershipType })} + membership. We shuid be in contact wi ye some time shuin. + `, + 'en-GB': ({ membershipType }: { membershipType: MembershipType }) => ` + You have successfully bought one year’s + ${translate('en-GB')(tMembershipType, { key: membershipType })} + membership. We should be in contact with you some time soon. + `, + }, +}; + +type Raw = typeof tPage; +export default tPage as TranslationsDictionary; diff --git a/src/pages/[locale]/furthsettins/lallans/[issueNumber]/index.astro b/src/pages/[locale]/furthsettins/lallans/[issueNumber]/index.astro index 48f1947..82fabf0 100644 --- a/src/pages/[locale]/furthsettins/lallans/[issueNumber]/index.astro +++ b/src/pages/[locale]/furthsettins/lallans/[issueNumber]/index.astro @@ -23,4 +23,5 @@ const issue = await getLallansIssue[issueNumber](); + diff --git a/src/pages/[locale]/furthsettins/scotsoun/[scotsounId]/index.astro b/src/pages/[locale]/furthsettins/scotsoun/[scotsounId]/index.astro index 90053fe..4244f70 100644 --- a/src/pages/[locale]/furthsettins/scotsoun/[scotsounId]/index.astro +++ b/src/pages/[locale]/furthsettins/scotsoun/[scotsounId]/index.astro @@ -24,4 +24,5 @@ const scotsoun = await getScotsoun[scotsounId](); + diff --git a/src/pages/[locale]/index.astro b/src/pages/[locale]/index.astro index e8b9cf0..d930c20 100644 --- a/src/pages/[locale]/index.astro +++ b/src/pages/[locale]/index.astro @@ -55,36 +55,11 @@ const mostRecentNewsItem = await getMostRecentNewsItem(locale); -
    -

    - -

    -

    -

    -

    - -

    -

    -

    -

    -

    {contactDetails.generalEnquiries.emailAddress} -
    - Scots Language Society
    - c/o 61 Cliffburn Road
    - Arbroath
    - Angus
    - DD11 5BA
    - United Kingdom
    -
    +
    ')} />

    diff --git a/src/pages/[locale]/jyne/index.astro b/src/pages/[locale]/jyne/index.astro new file mode 100644 index 0000000..7a1b4f6 --- /dev/null +++ b/src/pages/[locale]/jyne/index.astro @@ -0,0 +1,104 @@ +--- +import type Locale from '$types/Locale'; + +import { annualSubscriptionPrice } from '$data/subscriptionPrices'; +import contactDetails from '$data/contactDetails'; +import Page from '$layouts/Page.astro'; +import localeStaticPaths from '$lib/localeStaticPaths'; +import translate from '$i18n/translate'; +import tPage from '$i18n/translations/pages/jyne'; +import tMembershipType from '$i18n/translations/enums/membershipType'; +import { allMembershipTypes } from '$enums/MembershipType'; + +export async function getStaticPaths() { + return localeStaticPaths; +} + +const locale = Astro.params.locale as Locale; +const t = translate(locale); +--- + + +
    +

    +

    + +
    +

    +

    + + + + + + { + allMembershipTypes.map((membershipType) => ( + + )) + } + + + + + + { + allMembershipTypes.map((membershipType) => ( + + )) + } + + +
    {t(tPage, { key: 'subscription-price-table-caption' })}
    {t(tMembershipType, { key: membershipType })}
    + {new Intl.NumberFormat('en-GB', { + style: 'currency', + currency: 'GBP', + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }).format(annualSubscriptionPrice[membershipType])} +
    + +

    +

    + +
    +

    +

    +

    +

    +

    +
    diff --git a/src/pages/[locale]/jyne/paypal/[membershipType]/index.astro b/src/pages/[locale]/jyne/paypal/[membershipType]/index.astro new file mode 100644 index 0000000..0934854 --- /dev/null +++ b/src/pages/[locale]/jyne/paypal/[membershipType]/index.astro @@ -0,0 +1,41 @@ +--- +import type Locale from '$types/Locale'; +import { + type MembershipTypeSnakeCase, + allMembershipTypesSnakeCase, + snakeCaseToMembershipType, +} from '$enums/MembershipType'; +import { annualSubscriptionPrice } from '$data/subscriptionPrices'; +import Page from '$layouts/Page.astro'; +import PaypalProductButtons from '$components/PaypalProductButtons.astro'; +import localeStaticPaths from '$lib/localeStaticPaths'; +import translate from '$i18n/translate'; +import tJyne from '$i18n/translations/pages/jyne'; + +export async function getStaticPaths() { + return allMembershipTypesSnakeCase.flatMap((membershipType) => + localeStaticPaths.map((p) => ({ ...p, params: { ...p.params, membershipType } })) + ); +} + +const locale = Astro.params.locale as Locale; +const t = translate(locale); + +const membershipType = Astro.params.membershipType as MembershipTypeSnakeCase; +--- + + +
    +

    {t(tJyne, { key: `membership-type-${membershipType}` })}

    + +

    + + + +

    +
    diff --git a/src/pages/[locale]/jyne/paypal/[membershipType]/success.astro b/src/pages/[locale]/jyne/paypal/[membershipType]/success.astro new file mode 100644 index 0000000..569e294 --- /dev/null +++ b/src/pages/[locale]/jyne/paypal/[membershipType]/success.astro @@ -0,0 +1,30 @@ +--- +import type Locale from '$types/Locale'; +import { type MembershipTypeSnakeCase } from '$enums/MembershipType'; + +import Page from '$layouts/Page.astro'; +import { allMembershipTypesSnakeCase, snakeCaseToMembershipType } from '$enums/MembershipType'; +import localeStaticPaths from '$lib/localeStaticPaths'; +import translate from '$i18n/translate'; +import tJyne from '$i18n/translations/pages/jyne'; + +export async function getStaticPaths() { + return allMembershipTypesSnakeCase.flatMap((membershipType) => + localeStaticPaths.map((p) => ({ ...p, params: { ...p.params, membershipType } })) + ); +} + +const locale = Astro.params.locale as Locale; +const t = translate(locale); + +const membershipTypeSnakeCase = Astro.params.membershipType as MembershipTypeSnakeCase; +const membershipType = snakeCaseToMembershipType(membershipTypeSnakeCase); +--- + + +
    +

    {t(tJyne, { key: 'success' })}

    + +

    +

    +
    diff --git a/src/pages/[locale]/jyne/paypal/index.astro b/src/pages/[locale]/jyne/paypal/index.astro new file mode 100644 index 0000000..55905d1 --- /dev/null +++ b/src/pages/[locale]/jyne/paypal/index.astro @@ -0,0 +1,27 @@ +--- +import type Locale from '$types/Locale'; + +import Page from '$layouts/Page.astro'; +import localeStaticPaths from '$lib/localeStaticPaths'; +import translate from '$i18n/translate'; +import tJyne from '$i18n/translations/pages/jyne'; + +export async function getStaticPaths() { + return localeStaticPaths; +} + +const locale = Astro.params.locale as Locale; +const t = translate(locale); +--- + + +
    +

    +

    +

    +

    +
    diff --git a/src/scripts/wc/paypal-product-buttons.ts b/src/scripts/wc/paypal-product-buttons.ts new file mode 100644 index 0000000..ca06524 --- /dev/null +++ b/src/scripts/wc/paypal-product-buttons.ts @@ -0,0 +1,146 @@ +import { type PayPalNamespace, loadScript } from '@paypal/paypal-js'; + +const backendUrl = '/api'; + +class PaypalProductButtons extends HTMLElement { + constructor() { + super(); + + try { + const paypalClientId = this.getAttribute('paypal-client-id'); + + const paypalOptions = { + clientId: paypalClientId, + locale: 'en_GB', + commit: false, + currency: 'GBP', + intent: 'capture', + }; + + loadScript(paypalOptions) + .catch((err) => { + this.innerHTML = ` +
    + Failed to load PayPal buttons +

    ${err}

    +
    + `; + console.error(`Failed to load the PayPal JS SDK script. ${err}`); + }) + .then((paypal) => { + const constructButtons = paypal?.Buttons; + if (paypal && constructButtons) { + const successPageUrl = this.getAttribute('success-page-url'); + this.innerHTML = ''; + const div = document.createElement('div'); + this.appendChild(div); + this.makePaypalButtons(constructButtons, successPageUrl).render(div); + } + }) + .catch((err) => { + this.innerHTML = ` +
    + Failed to load PayPal buttons +

    ${err}

    +
    + `; + console.error(`Failed to construct the PayPal buttons. ${err}`); + }); + } catch (err) { + this.innerHTML = ` +
    + Failed to load PayPal buttons +

    ${err}

    +
    + `; + } + } + + makePaypalButtons( + constructButtons: Required['Buttons'], + successPageUrl: string + ) { + const productDescription = this.getAttribute('product-description'); + const shortDescription = this.getAttribute('short-description'); + const totalPrice = this.getAttribute('total-price'); + + return constructButtons({ + // This function is called to set up the transaction details without actually carrying it out + async createOrder() { + const body = JSON.stringify({ + productDescription, + shortDescription, + totalPrice, + }); + const headers = { + 'Content-Type': 'application/json', + }; + + let response: Response; + try { + response = await fetch(`${backendUrl}/order`, { method: 'POST', body, headers }); + } catch (err) { + throw new Error(`Failed to create order. ${err}`); + } + + if (!response.ok) { + throw new Error(` + Failed to create order. Backend response was + ${response.status} ${response.statusText}. ${await response.text()} + `); + } + + const { orderId, approvalLink } = await response.json(); + console.info(` + Successfully created order with ID + ${orderId}. Approval link: ${approvalLink} + `); + return orderId; + }, + + // This function is called after payment is confirmed + async onApprove(data) { + const body = JSON.stringify({ + id: data.orderID, + isApproved: true, + }); + const headers = { + 'Content-Type': 'application/json', + }; + + let response: Response; + try { + response = await fetch(`${backendUrl}/order`, { method: 'PATCH', body, headers }); + } catch (err) { + throw new Error(`Failed to capture order. ${err}`); + } + + if (!response.ok) { + throw new Error(` + Failed to capture order. Backend response was ${response.status} + ${response.statusText}. ${await response.text()} + `); + } + + const { orderId, requestStatus } = await response.json(); + console.info(` + Successfully captured order with ID ${orderId}, + request status is ${requestStatus}. + `); + + console.info(`Redirecting to confirmation page, ${successPageUrl}...`); + window.location.href = successPageUrl; + }, + }); + } + + getAttribute(key: string): string { + const value = super.getAttribute(key); + if (!value || typeof value !== 'string') { + throw new Error(`${key} attribute not provided to PaypalProductButtons component.`); + } + return value; + } +} + +customElements.define('paypal-product-buttons', PaypalProductButtons); diff --git a/src/types/TranslationsDictionary.d.ts b/src/types/TranslationsDictionary.d.ts index 5033ecd..47ee7f4 100644 --- a/src/types/TranslationsDictionary.d.ts +++ b/src/types/TranslationsDictionary.d.ts @@ -1,12 +1,15 @@ import type { defaultLocale } from '$i18n/locales'; import type Locale from './Locale'; -export type Translation = (params: { [key in keyof Params]: string }) => string; +export type Translation = (params: ParamsHash) => string; +export type LocalesTranslationHash = { + [locale in Locale]?: Translation; +} & { + [locale in typeof defaultLocale]: Translation; +}; export type TranslationsDictionary = { - readonly [key in keyof Raw]: { - [locale in Locale]?: Translation[0]>; - } & { - [locale in typeof defaultLocale]: Translation[0]>; - }; + readonly [key in keyof Raw]: LocalesTranslationHash< + Parameters[0] + >; }; diff --git a/tsconfig.json b/tsconfig.json index 61fcd49..956d1c9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,6 +11,7 @@ "$layouts/*": ["./layouts/*"], "$lib/*": ["./lib/*"], "$pages/*": ["./pages/*"], + "$scripts/*": ["./scripts/*"], "$types/*": ["./types/*"] }, "strictNullChecks": true -- cgit v1.2.3 From fab184fe597c1c833cd0afee66387160f3029bdf Mon Sep 17 00:00:00 2001 From: Joe Carstairs <118172583+jcarstairs-scottlogic@users.noreply.github.com> Date: Thu, 25 Jan 2024 17:01:13 +0000 Subject: Replaces a few straggler --margins with --spacing (#67) --- scss/modules/_link-gallery.xs.scss | 2 +- scss/modules/_nav.scss | 2 +- scss/modules/_nav.sm.scss | 2 +- scss/modules/_product.scss | 2 +- scss/modules/_schedule.scss | 4 ++-- scss/modules/_skip-to-content.scss | 4 ++-- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/scss/modules/_link-gallery.xs.scss b/scss/modules/_link-gallery.xs.scss index b2bf86c..805073d 100644 --- a/scss/modules/_link-gallery.xs.scss +++ b/scss/modules/_link-gallery.xs.scss @@ -1,7 +1,7 @@ .link-gallery { grid-auto-rows: calc( var(--gallery-row-image-height) + - var(--margin-block-sm) + + var(--spacing-block-sm) + var(--gallery-row-title-height) ); --gallery-item-column-span: 2; diff --git a/scss/modules/_nav.scss b/scss/modules/_nav.scss index f2f32a9..e2c984e 100644 --- a/scss/modules/_nav.scss +++ b/scss/modules/_nav.scss @@ -1,6 +1,6 @@ .nav { & > :not(:last-child) { - margin-block: 0 var(--margin-block-sm); + margin-block: 0 var(--spacing-block-sm); } li { diff --git a/scss/modules/_nav.sm.scss b/scss/modules/_nav.sm.scss index db540bd..688f947 100644 --- a/scss/modules/_nav.sm.scss +++ b/scss/modules/_nav.sm.scss @@ -9,7 +9,7 @@ + li::before { content: '/'; - margin-inline: var(--margin-inline-md); + margin-inline: var(--spacing-inline-md); } } } diff --git a/scss/modules/_product.scss b/scss/modules/_product.scss index bc05dfd..cb74573 100644 --- a/scss/modules/_product.scss +++ b/scss/modules/_product.scss @@ -10,7 +10,7 @@ flex-direction: column; margin-inline: auto; width: fit-content; - gap: var(--margin-block-sm); + gap: var(--spacing-block-sm); } .product__action-block img { diff --git a/scss/modules/_schedule.scss b/scss/modules/_schedule.scss index 40a9fee..fcb1304 100644 --- a/scss/modules/_schedule.scss +++ b/scss/modules/_schedule.scss @@ -1,10 +1,10 @@ .schedule:not(:first-child) { - margin-block-start: var(--margin-block-sm); + margin-block-start: var(--spacing-block-sm); } .schedule dt { font-weight: 700; - margin-block-start: var(--margin-block-sm); + margin-block-start: var(--spacing-block-sm); text-align: center; text-decoration: none; } diff --git a/scss/modules/_skip-to-content.scss b/scss/modules/_skip-to-content.scss index a752b29..0a5f365 100644 --- a/scss/modules/_skip-to-content.scss +++ b/scss/modules/_skip-to-content.scss @@ -7,7 +7,7 @@ } .skip-to-content:is(:focus, :focus-visible, :focus-within) { - top: var(--margin-block-md); + top: var(--spacing-block-md); opacity: 1; transition: top 100ms; -} \ No newline at end of file +} -- cgit v1.2.3 From 1f9d3bce4de8a3a89105862304ea1544106a18ee Mon Sep 17 00:00:00 2001 From: Joe Carstairs Date: Fri, 26 Jan 2024 14:13:45 +0000 Subject: Improves link gallery styling --- scss/modules/_index.sm.scss | 1 + scss/modules/_link-gallery.scss | 3 ++- scss/modules/_link-gallery.sm.scss | 4 ++++ scss/modules/_link-gallery.xs.scss | 4 ++++ 4 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 scss/modules/_link-gallery.sm.scss diff --git a/scss/modules/_index.sm.scss b/scss/modules/_index.sm.scss index a4fb988..ab51eb2 100644 --- a/scss/modules/_index.sm.scss +++ b/scss/modules/_index.sm.scss @@ -1,3 +1,4 @@ @use 'committee.sm' as *; +@use 'link-gallery.sm' as *; @use 'nav.sm' as *; diff --git a/scss/modules/_link-gallery.scss b/scss/modules/_link-gallery.scss index d321419..a7b7090 100644 --- a/scss/modules/_link-gallery.scss +++ b/scss/modules/_link-gallery.scss @@ -14,8 +14,9 @@ } img { - max-width: 100%; + max-width: 50%; max-height: 100%; + height: fit-content; margin: auto; object-fit: contain; } diff --git a/scss/modules/_link-gallery.sm.scss b/scss/modules/_link-gallery.sm.scss new file mode 100644 index 0000000..723dffc --- /dev/null +++ b/scss/modules/_link-gallery.sm.scss @@ -0,0 +1,4 @@ +.link-gallery { + --gallery-item-column-span: 3; +} + diff --git a/scss/modules/_link-gallery.xs.scss b/scss/modules/_link-gallery.xs.scss index 805073d..9bfc8d6 100644 --- a/scss/modules/_link-gallery.xs.scss +++ b/scss/modules/_link-gallery.xs.scss @@ -7,6 +7,10 @@ --gallery-item-column-span: 2; --gallery-row-title-height: 11ex; text-align: center; + + img { + max-width: 100%; + } } .link-gallery__title { -- cgit v1.2.3 From 85ee3a64d521a85a7aa2e655bb3a6d3368502372 Mon Sep 17 00:00:00 2001 From: Joe Carstairs Date: Fri, 26 Jan 2024 15:46:10 +0000 Subject: Adds .link-gallery--splash-first variant --- scss/modules/_link-gallery.scss | 67 +++++++++++++++++++++++++++----------- scss/modules/_link-gallery.xs.scss | 51 ++++++++++++++++++++--------- 2 files changed, 84 insertions(+), 34 deletions(-) diff --git a/scss/modules/_link-gallery.scss b/scss/modules/_link-gallery.scss index a7b7090..0f0c187 100644 --- a/scss/modules/_link-gallery.scss +++ b/scss/modules/_link-gallery.scss @@ -1,19 +1,37 @@ .link-gallery { - display: grid; - grid-template-columns: repeat(var(--grid-column-count), var(--grid-column-width)); - gap: var(--grid-gutter-width); padding-block: var(--spacing-block-md); + --gallery-item-column-span: 1; --gallery-row-image-height: 12rem; --gallery-row-title-height: fit-content; - text-align: center; - li { - list-style-type: none; - grid-column: span var(--gallery-item-column-span); + &:not(.link-gallery--splash-first) { + display: grid; + grid-template-columns: repeat(var(--grid-column-count), var(--grid-column-width)); + gap: var(--grid-gutter-width); + + li { + list-style-type: none; + grid-column: span var(--gallery-item-column-span); + + :is(&.link-gallery__container, & .link-gallery__container) { + display: grid; + grid-template-columns: 1fr; + grid-template-rows: var(--gallery-row-image-height) var(--gallery-row-title-height); + row-gap: var(--spacing-block-sm); + } + + .link-gallery__title { + width: 100%; + height: min-content; + text-align: center; + margin-inline: var(--spacing-inline-xs); + } + } } img { + display: block; max-width: 50%; max-height: 100%; height: fit-content; @@ -22,22 +40,33 @@ } } -.link-gallery__container { - display: grid; - grid-template-columns: 1fr; - grid-template-rows: var(--gallery-row-image-height) var(--gallery-row-title-height); - row-gap: var(--spacing-block-sm); -} - .link-gallery--furthsettins img { box-sizing: border-box; border-radius: 50%; border: solid var(--border-width) var(--text-color); } -.link-gallery__title { - width: 100%; - height: min-content; - text-align: center; - margin-inline: var(--spacing-inline-xs); +.link-gallery--splash-first { + .link-gallery__container { + display: block; + width: fit-content; + } + + li:first-of-type { + list-style-type: none; + + :is(&.link-gallery__container, & .link-gallery__container) { + width: 100%; + } + + .link-gallery__title { + text-align: center; + } + } + + li + li { + margin-block-start: var(--spacing-block-lg); + margin-inline-start: 1em; + } } + diff --git a/scss/modules/_link-gallery.xs.scss b/scss/modules/_link-gallery.xs.scss index 9bfc8d6..a8f48ac 100644 --- a/scss/modules/_link-gallery.xs.scss +++ b/scss/modules/_link-gallery.xs.scss @@ -1,25 +1,46 @@ .link-gallery { - grid-auto-rows: calc( - var(--gallery-row-image-height) + - var(--spacing-block-sm) + - var(--gallery-row-title-height) - ); --gallery-item-column-span: 2; --gallery-row-title-height: 11ex; - text-align: center; + + &:not(.link-gallery--splash-first) { + grid-auto-rows: calc( + var(--gallery-row-image-height) + + var(--spacing-block-sm) + + var(--gallery-row-title-height) + ); + + .link-gallery__title { + width: 100%; + height: 9.9ex; + height: 3lh; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; + } + } img { max-width: 100%; } } -.link-gallery__title { - width: 100%; - height: 9.9ex; - height: 3lh; - text-overflow: ellipsis; - display: -webkit-box; - -webkit-line-clamp: 3; - -webkit-box-orient: vertical; - overflow: hidden; +.link-gallery--splash-first li:first-of-type { + :is(&.link-gallery__container, & .link-gallery__container) { + display: grid; + grid-template-columns: repeat(var(--grid-column-count), var(--grid-column-width)); + gap: var(--grid-gutter-width); + } + + img { + grid-column: span 2; + } + + .link-gallery__title { + grid-column: span calc(var(--grid-prose-column-count) - 2); + align-self: center; + text-align: start; + } } + -- cgit v1.2.3 From 87a291cc6b79610cc48e804fb94e6ab558a05528 Mon Sep 17 00:00:00 2001 From: Joe Carstairs Date: Fri, 26 Jan 2024 15:46:42 +0000 Subject: Lallans gallery splashes most recent issue only --- src/components/LallansIssuesGallery.astro | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/components/LallansIssuesGallery.astro b/src/components/LallansIssuesGallery.astro index 3e79de9..f4c8e16 100644 --- a/src/components/LallansIssuesGallery.astro +++ b/src/components/LallansIssuesGallery.astro @@ -10,19 +10,37 @@ const issues = (await getAllLallansIssues()).sort(issueNumberDescending); function issueNumberDescending(issue1: LallansIssue, issue2: LallansIssue) { return issue2.issueNumber - issue1.issueNumber; } + +const mostRecentIssue = issues[0]; +const allOtherIssues = issues.slice(1); --- -