Browse Source

Add iceform

Use iceform for receiving contact form details.
master
TheoryOfNekomata 1 month ago
parent
commit
afb9c2ee73
10 changed files with 123 additions and 42 deletions
  1. +6
    -7
      packages/web/package.json
  2. +43
    -12
      packages/web/pnpm-lock.yaml
  3. +7
    -2
      packages/web/src/components/molecules/ContactCtaBanner/index.tsx
  4. +4
    -0
      packages/web/src/components/organisms/IndexLayout/index.tsx
  5. +5
    -0
      packages/web/src/handlers/contact.ts
  6. +14
    -0
      packages/web/src/pages/a/contact.ts
  7. +11
    -0
      packages/web/src/pages/api/contact.ts
  8. +0
    -13
      packages/web/src/pages/api/hello.ts
  9. +18
    -4
      packages/web/src/pages/contact/index.tsx
  10. +15
    -4
      packages/web/src/pages/index.tsx

+ 6
- 7
packages/web/package.json View File

@@ -11,11 +11,12 @@
"dependencies": {
"@tesseract-design/viewfinder-base": "0.0.1",
"@tesseract-design/viewfinder-react": "0.0.1",
"@tesseract-design/web-action-react": "^0.2.0",
"@tesseract-design/web-formatted-react": "^0.2.0",
"@tesseract-design/web-freeform-react": "^0.2.0",
"@tesseract-design/web-navigation-react": "^0.2.0",
"@theoryofnekomata/formxtra": "^1.0.3",
"@tesseract-design/web-action-react": "0.2.0",
"@tesseract-design/web-formatted-react": "0.2.1",
"@tesseract-design/web-freeform-react": "0.2.1",
"@tesseract-design/web-navigation-react": "0.2.1",
"@theoryofnekomata/formxtra": "../../../formxtra",
"@modal-sh/iceform-next": "0.0.0",
"@types/node": "20.6.0",
"@types/react": "18.2.21",
"@types/react-dom": "18.2.7",
@@ -25,8 +26,6 @@
"eslint-config-next": "13.4.19",
"next": "13.4.19",
"postcss": "8.4.29",
"react": "18.2.0",
"react-dom": "18.2.0",
"tailwindcss": "3.3.3",
"typescript": "5.2.2"
}


+ 43
- 12
packages/web/pnpm-lock.yaml View File

@@ -5,6 +5,9 @@ settings:
excludeLinksFromLockfile: false

dependencies:
'@modal-sh/iceform-next':
specifier: 0.0.0
version: 0.0.0(next@13.4.19)(react-dom@18.2.0)(react@18.2.0)
'@tesseract-design/viewfinder-base':
specifier: 0.0.1
version: 0.0.1
@@ -12,20 +15,20 @@ dependencies:
specifier: 0.0.1
version: 0.0.1(react-dom@18.2.0)(react@18.2.0)
'@tesseract-design/web-action-react':
specifier: ^0.2.0
specifier: 0.2.0
version: 0.2.0(react-dom@18.2.0)(react@18.2.0)
'@tesseract-design/web-formatted-react':
specifier: ^0.2.0
specifier: 0.2.1
version: 0.2.1(react-dom@18.2.0)(react@18.2.0)
'@tesseract-design/web-freeform-react':
specifier: ^0.2.0
specifier: 0.2.1
version: 0.2.1(react-dom@18.2.0)(react@18.2.0)
'@tesseract-design/web-navigation-react':
specifier: ^0.2.0
specifier: 0.2.1
version: 0.2.1(react-dom@18.2.0)(react@18.2.0)
'@theoryofnekomata/formxtra':
specifier: ^1.0.3
version: 1.0.3
specifier: ../../../formxtra
version: link:../../../formxtra
'@types/node':
specifier: 20.6.0
version: 20.6.0
@@ -53,12 +56,6 @@ dependencies:
postcss:
specifier: 8.4.29
version: 8.4.29
react:
specifier: 18.2.0
version: 18.2.0
react-dom:
specifier: 18.2.0
version: 18.2.0(react@18.2.0)
tailwindcss:
specifier: 3.3.3
version: 3.3.3
@@ -179,6 +176,23 @@ packages:
'@jridgewell/sourcemap-codec': 1.4.15
dev: false

/@modal-sh/iceform-next@0.0.0(next@13.4.19)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-/FoVjAtY0PCAPOLLN53vLjlJs3mIMmmy5r+Iqg7dp5csburzkGtLpYRpzMAVYNj4ttV0j+YaJxSBYWNtT+PaUQ==}
engines: {node: '>=12'}
peerDependencies:
next: 13.4.19
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
dependencies:
'@theoryofnekomata/formxtra': 1.0.3
busboy: 1.6.0
next: 13.4.19(react-dom@18.2.0)(react@18.2.0)
node-cache: 5.1.2
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
seroval: 0.10.4
dev: false

/@modal-sh/react-utils@0.0.0(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-aqbD221Zhsucxnr2ZJqS2sZQbI4/xcb703GvcXLew+2zqFhZOwfRdclwTralB/TXBmrrIh3aHeOisQ+DLlmC3w==}
engines: {node: '>=12'}
@@ -826,6 +840,11 @@ packages:
resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
dev: false

/clone@2.1.2:
resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==}
engines: {node: '>=0.8'}
dev: false

/clsx@1.2.1:
resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==}
engines: {node: '>=6'}
@@ -2159,6 +2178,13 @@ packages:
- babel-plugin-macros
dev: false

/node-cache@5.1.2:
resolution: {integrity: sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==}
engines: {node: '>= 8.0.0'}
dependencies:
clone: 2.1.2
dev: false

/node-releases@2.0.14:
resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==}
dev: false
@@ -2603,6 +2629,11 @@ packages:
lru-cache: 6.0.0
dev: false

/seroval@0.10.4:
resolution: {integrity: sha512-TdaE9JkoATjKu+vjwllieX8zWyBTUVxbgWDnOsDJFfmKbM7vLSukuCXuD3pO3kkCtX4daywOW8ps2VCdPhS8/w==}
engines: {node: '>=10'}
dev: false

/set-function-length@1.2.2:
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
engines: {node: '>= 0.4'}


+ 7
- 2
packages/web/src/components/molecules/ContactCtaBanner/index.tsx View File

@@ -1,4 +1,5 @@
import * as React from 'react';
import * as Iceform from '@modal-sh/iceform-next';
import {Layouts} from '@tesseract-design/viewfinder-react';
import * as WebNavigationReact from '@tesseract-design/web-navigation-react';
import * as WebFreeformReact from '@tesseract-design/web-freeform-react';
@@ -10,12 +11,14 @@ export interface ContactCtaBannerProps {
onSubmit?: React.FormEventHandler<HTMLFormElement>;
visible?: boolean;
hasBrand?: boolean;
iceformProps?: Partial<ReturnType<typeof Iceform.useResponse>>;
}

export const ContactCtaBanner: React.FC<ContactCtaBannerProps> = ({
onSubmit,
visible: visibleProp = false,
hasBrand = false,
iceformProps = {},
}) => {
const [visible, setVisible] = React.useState<boolean>();
const autofocusRef = React.useRef<HTMLInputElement>(null);
@@ -76,12 +79,14 @@ export const ContactCtaBanner: React.FC<ContactCtaBannerProps> = ({
</div>
</div>
{visible !== false && (
<form
<Iceform.Form
{...iceformProps}
className="mt-8"
aria-label="Contact Form"
onSubmit={onSubmit}
method="post"
action="/a/contact"
clientAction="/api/contact"
>
<fieldset className="contents">
<legend className="sr-only">
@@ -131,7 +136,7 @@ export const ContactCtaBanner: React.FC<ContactCtaBannerProps> = ({
</div>
</div>
</fieldset>
</form>
</Iceform.Form>
)}
</Layouts.Basic.ContentContainer>
</div>


+ 4
- 0
packages/web/src/components/organisms/IndexLayout/index.tsx View File

@@ -1,4 +1,5 @@
import * as React from 'react';
import * as Iceform from '@modal-sh/iceform-next';
import {Layouts} from '@tesseract-design/viewfinder-react';
import {MainLandingSection, MainLandingSectionProps} from '@/components/molecules/MainLandingSection';
import {MakeSection, MakeSectionProps} from '@/components/molecules/MakeSection';
@@ -17,6 +18,7 @@ export interface IndexLayoutProps {
contactVisible?: boolean;
hasMainLandingSection?: boolean;
contactHasBrand?: boolean;
iceformProps?: Partial<ReturnType<typeof Iceform.useResponse>>
}

export const IndexLayout: React.FC<IndexLayoutProps> = ({
@@ -28,6 +30,7 @@ export const IndexLayout: React.FC<IndexLayoutProps> = ({
contactVisible = false,
hasMainLandingSection = false,
contactHasBrand = false,
iceformProps,
}) => (
<Layouts.Basic.Root className="contents">
<div
@@ -65,6 +68,7 @@ export const IndexLayout: React.FC<IndexLayoutProps> = ({
onSubmit={onSubmit}
visible={contactVisible}
hasBrand={contactHasBrand}
iceformProps={iceformProps}
/>
<Footer/>
</Layouts.Basic.Root>


+ 5
- 0
packages/web/src/handlers/contact.ts View File

@@ -0,0 +1,5 @@
import { NextApiHandler } from 'next';

export const receiveContactEntry: NextApiHandler = async (req, res) => {
res.status(200).json(req.body);
};

+ 14
- 0
packages/web/src/pages/a/contact.ts View File

@@ -0,0 +1,14 @@
import { NextPage } from 'next';
import * as Iceform from '@modal-sh/iceform-next';
import { receiveContactEntry } from '@/handlers/contact';

const ActionContactPage: NextPage = () => null;

const getServerSideProps = Iceform.action.getServerSideProps({
fn: receiveContactEntry,
});

export {
getServerSideProps,
ActionContactPage as default,
};

+ 11
- 0
packages/web/src/pages/api/contact.ts View File

@@ -0,0 +1,11 @@
import * as Iceform from '@modal-sh/iceform-next';
import { receiveContactEntry } from '@/handlers/contact';

const handler = Iceform.action.wrapApiHandler({ fn: receiveContactEntry });

const config = Iceform.action.getApiConfig();

export {
config,
handler as default,
};

+ 0
- 13
packages/web/src/pages/api/hello.ts View File

@@ -1,13 +0,0 @@
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from 'next'

type Data = {
name: string
}

export default function handler(
req: NextApiRequest,
res: NextApiResponse<Data>
) {
res.status(200).json({ name: 'John Doe' })
}

+ 18
- 4
packages/web/src/pages/contact/index.tsx View File

@@ -1,8 +1,8 @@
import {NextPage} from 'next';
import {useHuePulsate} from '@/hooks/effects';
import * as config from '@/config';
import * as React from 'react';
import * as Iceform from '@modal-sh/iceform-next';
import * as config from '@/config';
import {IndexLayout} from '@/components/organisms/IndexLayout';
import {useHuePulsate} from '@/hooks/effects';

const IMAGE_CHOICES = [
'/images/3cd237361eada7fd30eb96d42d55ec00.jpg',
@@ -20,7 +20,18 @@ const IMAGE_CHOICES = [
'/images/fe3050a31d3fb86accf26cc4bebec102.png',
];

const ContactPage: NextPage = () => {
const ContactPage: Iceform.NextPage = ({
req,
res,
}) => {
const {
response,
loading,
...iceformProps
} = Iceform.useResponse({
res,
});

useHuePulsate(config.effects.huePulsate);

return (
@@ -28,8 +39,11 @@ const ContactPage: NextPage = () => {
backgroundImages={IMAGE_CHOICES}
contactVisible
contactHasBrand
iceformProps={iceformProps}
/>
)
};

export const getServerSideProps = Iceform.destination.getServerSideProps();

export default ContactPage;

+ 15
- 4
packages/web/src/pages/index.tsx View File

@@ -4,6 +4,7 @@ import {useHuePulsate} from '@/hooks/effects';
import {useContactForm} from '@/hooks/contact';
import {IndexLayout} from '@/components/organisms/IndexLayout';
import * as config from '@/config';
import * as Iceform from '@modal-sh/iceform-next';

const IMAGE_CHOICES = [
'/images/3cd237361eada7fd30eb96d42d55ec00.jpg',
@@ -122,21 +123,31 @@ const featuredProjectData = [
},
];

const IndexPage: NextPage = () => {
const IndexPage: Iceform.NextPage = ({
req,
res,
}) => {
const {
response,
loading,
...iceformProps
} = Iceform.useResponse({
res,
});
useHuePulsate(config.effects.huePulsate);

const { processContactForm } = useContactForm();

return (
<IndexLayout
backgroundImages={IMAGE_CHOICES}
makeSectionData={makeSectionData}
envisionSectionData={envisionSectionData}
featuredProjectData={featuredProjectData}
onSubmit={processContactForm}
hasMainLandingSection
iceformProps={iceformProps}
/>
);
};

export const getServerSideProps = Iceform.destination.getServerSideProps();

export default IndexPage;

Loading…
Cancel
Save