@tfehotels/tfe-gatsby-library 1.0.0
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.
- package/README.md +41 -0
- package/lib/index.browser.cjs +2 -0
- package/lib/index.browser.cjs.map +1 -0
- package/lib/index.browser.d.ts +640 -0
- package/lib/index.browser.esm.js +2 -0
- package/lib/index.browser.esm.js.map +1 -0
- package/lib/index.node.cjs +2 -0
- package/lib/index.node.cjs.map +1 -0
- package/lib/index.node.d.ts +116 -0
- package/lib/index.node.esm.js +2 -0
- package/lib/index.node.esm.js.map +1 -0
- package/package.json +84 -0
- package/src/browser/api/button/index.tsx +16 -0
- package/src/browser/api/button/types.ts +7 -0
- package/src/browser/api/checkbox/index.tsx +74 -0
- package/src/browser/api/checkbox/types.tsx +21 -0
- package/src/browser/api/country-prefix/index.tsx +150 -0
- package/src/browser/api/country-prefix/types.tsx +17 -0
- package/src/browser/api/form/index.tsx +330 -0
- package/src/browser/api/form/types.ts +31 -0
- package/src/browser/api/google-places/index.tsx +90 -0
- package/src/browser/api/google-places/types.ts +24 -0
- package/src/browser/api/icon/index.tsx +26 -0
- package/src/browser/api/icon/types.tsx +24 -0
- package/src/browser/api/image/index.tsx +91 -0
- package/src/browser/api/image/types.ts +11 -0
- package/src/browser/api/input/index.tsx +44 -0
- package/src/browser/api/input/types.ts +10 -0
- package/src/browser/api/link/index.tsx +54 -0
- package/src/browser/api/link/types.ts +24 -0
- package/src/browser/api/linklist/index.tsx +17 -0
- package/src/browser/api/linklist/types.ts +6 -0
- package/src/browser/api/select/index.tsx +99 -0
- package/src/browser/api/select/types.ts +24 -0
- package/src/browser/api/svg/index.tsx +8 -0
- package/src/browser/api/svg/types.ts +8 -0
- package/src/browser/api/text/index.tsx +14 -0
- package/src/browser/api/text/types.ts +12 -0
- package/src/browser/api/textarea/index.tsx +12 -0
- package/src/browser/api/title/index.tsx +19 -0
- package/src/browser/api/title/types.ts +10 -0
- package/src/browser/api/types.ts +245 -0
- package/src/browser/carousel/buttons/index.tsx +81 -0
- package/src/browser/carousel/buttons/types.ts +15 -0
- package/src/browser/carousel/dots/index.tsx +53 -0
- package/src/browser/carousel/dots/types.ts +14 -0
- package/src/browser/carousel/index.tsx +131 -0
- package/src/browser/carousel/types.ts +21 -0
- package/src/browser/markdown/index.tsx +41 -0
- package/src/browser/markdown/types.ts +11 -0
- package/src/browser/modal/index.tsx +35 -0
- package/src/browser/modal/types.ts +9 -0
- package/src/browser/spinner/index.tsx +19 -0
- package/src/browser/spinner/types.ts +5 -0
- package/src/browser/toast/index.tsx +84 -0
- package/src/browser/use_viewport/index.tsx +34 -0
- package/src/browser/use_viewport/types.ts +5 -0
- package/src/browser/utils/animation.ts +12 -0
- package/src/browser/utils/booking_engine.ts +180 -0
- package/src/browser/utils/eclub.ts +30 -0
- package/src/browser/utils/forms.ts +76 -0
- package/src/browser/utils/hotel.ts +25 -0
- package/src/browser/utils/image.ts +63 -0
- package/src/browser/utils/location.ts +213 -0
- package/src/browser/utils/notifications.tsx +25 -0
- package/src/browser/utils/number.ts +6 -0
- package/src/browser/utils/requests.ts +2 -0
- package/src/browser/utils/search.ts +25 -0
- package/src/browser/utils/string.ts +9 -0
- package/src/browser/utils/types.ts +106 -0
- package/src/browser/utils/url.ts +116 -0
- package/src/browser/utils/viewport.ts +59 -0
- package/src/index.browser.ts +103 -0
- package/src/index.node.ts +16 -0
- package/src/node/api/index.ts +174 -0
- package/src/node/api/types.ts +19 -0
- package/src/node/build/index.ts +142 -0
- package/src/node/build/types.ts +5 -0
- package/src/node/config/index.ts +149 -0
- package/src/node/form/index.ts +23 -0
- package/src/node/form/types.ts +3 -0
- package/src/node/property/index.ts +78 -0
- package/src/node/property/types.ts +25 -0
- package/src/node/url/index.ts +8 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { join_paths } from "../url";
|
|
2
|
+
import { ImageType, PageTagsType, QueryParamsType } from "./types";
|
|
3
|
+
const { getImage, withArtDirection } = module.require("gatsby-plugin-image");
|
|
4
|
+
|
|
5
|
+
export const tagExists = (
|
|
6
|
+
pageTags: PageTagsType,
|
|
7
|
+
...tags: string[]
|
|
8
|
+
): boolean => {
|
|
9
|
+
for (const tag of Object.keys(pageTags)) {
|
|
10
|
+
if (tags.includes(tag)) {
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return false;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const api_link = (
|
|
18
|
+
apiPath: string = "",
|
|
19
|
+
queryParams: QueryParamsType | null = null
|
|
20
|
+
): string =>
|
|
21
|
+
`${join_paths(
|
|
22
|
+
`${process.env.API_PROTOCOL}://${process.env.API_HOST}`,
|
|
23
|
+
apiPath
|
|
24
|
+
)}${queryParams ? `?${new URLSearchParams(queryParams).toString()}` : ""}`;
|
|
25
|
+
|
|
26
|
+
export const buildImage = (img: ImageType) => {
|
|
27
|
+
if (!img) {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let fallback = {
|
|
32
|
+
src: img.url,
|
|
33
|
+
sizes: "100vw",
|
|
34
|
+
srcSet: "",
|
|
35
|
+
};
|
|
36
|
+
let sources = [];
|
|
37
|
+
let placeholder = { fallback: img.url };
|
|
38
|
+
const sizes = [img.width, 360, 769, 1024, 1216, 1408].sort((a, b) => a - b);
|
|
39
|
+
|
|
40
|
+
if (img.optimized) {
|
|
41
|
+
fallback.src = img.url + img.name + "-" + img.width + "w.jpg";
|
|
42
|
+
placeholder.fallback = img.url + img.name + "-20w.jpg";
|
|
43
|
+
sources.push({ sizes: "100vw", srcSet: "", type: "image/avif" });
|
|
44
|
+
sources.push({ sizes: "100vw", srcSet: "", type: "image/webp" });
|
|
45
|
+
for (let index in sizes) {
|
|
46
|
+
if (sizes[index] > img.width) {
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
let image_url =
|
|
50
|
+
(index === "0" ? "" : ",") + img.url + img.name + "-" + sizes[index];
|
|
51
|
+
fallback.srcSet += image_url + "w.jpg " + sizes[index] + "w";
|
|
52
|
+
sources[0].srcSet += image_url + "w.avif " + sizes[index] + "w";
|
|
53
|
+
sources[1].srcSet += image_url + "w.webp " + sizes[index] + "w";
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return getImage({
|
|
58
|
+
childImageSharp: {
|
|
59
|
+
gatsbyImageData: {
|
|
60
|
+
backgroundColor: "transparent",
|
|
61
|
+
height: img.height / img.width,
|
|
62
|
+
width: 1,
|
|
63
|
+
layout: "fullWidth",
|
|
64
|
+
placeholder: placeholder,
|
|
65
|
+
images: {
|
|
66
|
+
fallback: fallback,
|
|
67
|
+
sources: sources,
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export const getImageData = (image: ImageType, mobileImage: ImageType) => {
|
|
75
|
+
const image_data = buildImage(image);
|
|
76
|
+
const mobile_image_data = buildImage(mobileImage);
|
|
77
|
+
return image_data && mobile_image_data // If mobile image exists, it must be added using Art Direction.
|
|
78
|
+
? withArtDirection(image_data, [
|
|
79
|
+
{
|
|
80
|
+
media: "(max-width: 768px)",
|
|
81
|
+
image: mobile_image_data,
|
|
82
|
+
},
|
|
83
|
+
])
|
|
84
|
+
: image_data;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export const extractForm = (formSection: any) => {
|
|
88
|
+
let form: any = {
|
|
89
|
+
settings: {
|
|
90
|
+
pk: 0,
|
|
91
|
+
name: "",
|
|
92
|
+
form_method: "post",
|
|
93
|
+
form_submit: api_link("/v1/project/request/contact"),
|
|
94
|
+
form_redirection: null,
|
|
95
|
+
form_ajax_request: null,
|
|
96
|
+
recaptcha_action: "",
|
|
97
|
+
recaptcha_v3: false,
|
|
98
|
+
},
|
|
99
|
+
items: [],
|
|
100
|
+
fields: {},
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
form.settings.recaptcha_v3 = !!formSection.recaptcha_v3;
|
|
104
|
+
form.settings.recaptcha_action =
|
|
105
|
+
formSection.data.recaptcha_action ?? formSection.name;
|
|
106
|
+
form.settings.pk = formSection.pk;
|
|
107
|
+
form.settings.name = formSection.name;
|
|
108
|
+
[
|
|
109
|
+
"form_submit",
|
|
110
|
+
"form_recipient",
|
|
111
|
+
"form_method",
|
|
112
|
+
"form_redirection",
|
|
113
|
+
"form_ajax_request",
|
|
114
|
+
"form_success_msg",
|
|
115
|
+
].map((cfg) => {
|
|
116
|
+
if (formSection.hasOwnProperty(cfg)) {
|
|
117
|
+
// @ts-ignore
|
|
118
|
+
form.settings[cfg] = formSection[cfg];
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
const validTypes = new Set([
|
|
122
|
+
"checkbox",
|
|
123
|
+
"input",
|
|
124
|
+
"select",
|
|
125
|
+
"button",
|
|
126
|
+
"google-places",
|
|
127
|
+
"country-prefix",
|
|
128
|
+
"textarea",
|
|
129
|
+
"recaptcha2",
|
|
130
|
+
"title",
|
|
131
|
+
"text",
|
|
132
|
+
"link",
|
|
133
|
+
"image",
|
|
134
|
+
]);
|
|
135
|
+
let uniqueId = 0;
|
|
136
|
+
const addItem = (parents: any, newItem: any): any => {
|
|
137
|
+
let item = form.items;
|
|
138
|
+
for (let key of parents) {
|
|
139
|
+
item = item[key];
|
|
140
|
+
if (!Array.isArray(item)) {
|
|
141
|
+
item = item.children;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
item.push(newItem);
|
|
145
|
+
};
|
|
146
|
+
// Set Form fields:
|
|
147
|
+
const setFormFields = (field: any, parents: any, k: number) => {
|
|
148
|
+
let fieldName =
|
|
149
|
+
field.input_name && !form.fields[field.input_name]
|
|
150
|
+
? field.input_name
|
|
151
|
+
: field.name ?? `${field.type}${uniqueId++}`;
|
|
152
|
+
|
|
153
|
+
if ("section" === field.type) {
|
|
154
|
+
const sectionName = field.name ?? `sec_${parents.length}-${k}`;
|
|
155
|
+
addItem(parents, { name: sectionName, children: [] });
|
|
156
|
+
field.children.map((field: any, k2: any) =>
|
|
157
|
+
setFormFields(field, [...parents, k], k2)
|
|
158
|
+
);
|
|
159
|
+
} else if (validTypes.has(field.type)) {
|
|
160
|
+
addItem(parents, fieldName);
|
|
161
|
+
form.fields[fieldName] = field;
|
|
162
|
+
// Pre-process images (fixes flickering issue)
|
|
163
|
+
if (field.type === "image") {
|
|
164
|
+
form.fields[fieldName].imageData = getImageData(
|
|
165
|
+
field.image,
|
|
166
|
+
field.mobile_image
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
formSection.children.map((field: any, k: any) => setFormFields(field, [], k));
|
|
173
|
+
return form;
|
|
174
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface PageTagsType {
|
|
2
|
+
[k: string]: string;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export interface QueryParamsType {
|
|
6
|
+
[key: string]: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ImageType {
|
|
10
|
+
type: "image";
|
|
11
|
+
url: string;
|
|
12
|
+
optimized: boolean;
|
|
13
|
+
name: string;
|
|
14
|
+
alt: string | null;
|
|
15
|
+
caption: string | null;
|
|
16
|
+
width: number;
|
|
17
|
+
height: number;
|
|
18
|
+
extension: string;
|
|
19
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { Actions } from "gatsby";
|
|
2
|
+
import { RedirectionType } from "./types";
|
|
3
|
+
const fs = module.require("fs");
|
|
4
|
+
|
|
5
|
+
// Build page elements (only for the ones having a build.js)
|
|
6
|
+
export const build = async (page: any) => {
|
|
7
|
+
let body = [];
|
|
8
|
+
for (const element of page.body) {
|
|
9
|
+
body.push(
|
|
10
|
+
await buildElement(element, {
|
|
11
|
+
slug: page.slug,
|
|
12
|
+
tags: page.tags,
|
|
13
|
+
data: page.data,
|
|
14
|
+
})
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
page.body = body;
|
|
18
|
+
return page;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Generate API pages
|
|
23
|
+
*
|
|
24
|
+
* @param actions Gatsby actions: https://www.gatsbyjs.com/docs/reference/config-files/actions/
|
|
25
|
+
*/
|
|
26
|
+
export const createAPIPages = async (actions: Actions) => {
|
|
27
|
+
if (!process.env.GATSBY_PROJECT_ROOT) {
|
|
28
|
+
throw new Error(
|
|
29
|
+
'Please define "process.env.GATSBY_PROJECT_ROOT = __dirname" within the ./gatsby-config.js file.'
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
const { createPage, createRedirect } = actions;
|
|
33
|
+
// Generate Pages
|
|
34
|
+
const jsonDoc = JSON.parse(
|
|
35
|
+
fs.readFileSync(
|
|
36
|
+
`${process.env.GATSBY_PROJECT_ROOT}/src/data/pages/full-content.json`,
|
|
37
|
+
`utf-8`
|
|
38
|
+
)
|
|
39
|
+
);
|
|
40
|
+
let excluded = []; // List of excluded pages
|
|
41
|
+
for (const page of jsonDoc) {
|
|
42
|
+
if (page.is_hidden) {
|
|
43
|
+
excluded.push(page.slug);
|
|
44
|
+
}
|
|
45
|
+
// Skip disabled pages
|
|
46
|
+
if (!page.is_active) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
// Skip when "DEV_PAGE" is defined on "./.env.development" and doesn't match current page:
|
|
50
|
+
if (
|
|
51
|
+
process.env.DEV_PAGE &&
|
|
52
|
+
page.id !== parseInt(`${process.env.DEV_PAGE}`)
|
|
53
|
+
) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
createPage({
|
|
58
|
+
path: page.slug,
|
|
59
|
+
component: require.resolve(
|
|
60
|
+
`${process.env.GATSBY_PROJECT_ROOT}/src/components/page/index.tsx`
|
|
61
|
+
),
|
|
62
|
+
context: await build(page),
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
createAPIRedirections(createRedirect);
|
|
67
|
+
// Generate the file "./src/data/excluded.json" containing the pages which should be excluded on the sitemap and disallowed within robots.txt.
|
|
68
|
+
fs.writeFileSync(
|
|
69
|
+
`${process.env.GATSBY_PROJECT_ROOT}/src/data/pages/excluded.json`,
|
|
70
|
+
JSON.stringify(excluded)
|
|
71
|
+
);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Generate API redirections
|
|
76
|
+
*/
|
|
77
|
+
export const createAPIRedirections = (
|
|
78
|
+
createRedirect: Actions["createRedirect"]
|
|
79
|
+
) => {
|
|
80
|
+
const redirections = JSON.parse(
|
|
81
|
+
fs.readFileSync(
|
|
82
|
+
`${process.env.GATSBY_PROJECT_ROOT}/src/data/pages/redirections.json`,
|
|
83
|
+
`utf-8`
|
|
84
|
+
)
|
|
85
|
+
);
|
|
86
|
+
redirections.forEach((redirect: RedirectionType) => {
|
|
87
|
+
createRedirect({
|
|
88
|
+
fromPath: redirect.page,
|
|
89
|
+
toPath: redirect.redirection,
|
|
90
|
+
isPermanent: redirect.type === "301",
|
|
91
|
+
force: true,
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Function executed after creating pages to perform following tasks:
|
|
98
|
+
* - Creation of "robots.txt"
|
|
99
|
+
*/
|
|
100
|
+
export const postBuild = () => {
|
|
101
|
+
const excludedPages = JSON.parse(
|
|
102
|
+
fs.readFileSync(
|
|
103
|
+
`${process.env.GATSBY_PROJECT_ROOT}/src/data/pages/excluded.json`,
|
|
104
|
+
`utf-8`
|
|
105
|
+
)
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
const robotsTxt = [
|
|
109
|
+
"User-agent: *",
|
|
110
|
+
`${excludedPages.length ? excludedPages.map((slug: string) => `Disallow: ${slug}`).join("\n") : "Allow: /"}`,
|
|
111
|
+
`Sitemap: ${process.env.PROTOCOL}://${process.env.HOST}/sitemap/sitemap-index.xml`,
|
|
112
|
+
`Host: ${process.env.PROTOCOL}://${process.env.HOST}`,
|
|
113
|
+
];
|
|
114
|
+
|
|
115
|
+
fs.writeFileSync(
|
|
116
|
+
`${process.env.GATSBY_PROJECT_ROOT}/public/robots.txt`,
|
|
117
|
+
robotsTxt.join("\n")
|
|
118
|
+
);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Builds API Element (usually for "section" and "data" elements)
|
|
123
|
+
* This generates a initial data structure for the section/data to avoid processing the React component
|
|
124
|
+
* initial state on every single render.
|
|
125
|
+
*
|
|
126
|
+
* @param element API element
|
|
127
|
+
* @param context
|
|
128
|
+
* @returns
|
|
129
|
+
*/
|
|
130
|
+
export const buildElement = async (element: any, context: any) => {
|
|
131
|
+
const path = `${process.env.GATSBY_PROJECT_ROOT}/src/components/element/${element.type}/${element.name}/build.js`;
|
|
132
|
+
if (fs.existsSync(path)) {
|
|
133
|
+
const module = await import(path);
|
|
134
|
+
if (module?.build) {
|
|
135
|
+
return await module.build(element, context);
|
|
136
|
+
}
|
|
137
|
+
// Show warning on development:
|
|
138
|
+
} else if (process.env.ENV === "development") {
|
|
139
|
+
console.warn(`build.js file not found: ${path}`);
|
|
140
|
+
}
|
|
141
|
+
return element;
|
|
142
|
+
};
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
export const defaultGatsbyConfig = (config: any = {}, plugins: any[] = []) => {
|
|
2
|
+
if (!process.env.GATSBY_PROJECT_ROOT) {
|
|
3
|
+
throw new Error(
|
|
4
|
+
'Please define "process.env.GATSBY_PROJECT_ROOT = __dirname" within the ./gatsby-config.js file.'
|
|
5
|
+
);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
return {
|
|
9
|
+
siteMetadata: {
|
|
10
|
+
title: process.env.SITE_TITLE,
|
|
11
|
+
description: process.env.SITE_DESCRIPTION,
|
|
12
|
+
siteUrl: `${process.env.PROTOCOL}://${process.env.HOST}`,
|
|
13
|
+
image: process.env.SITE_IMAGE,
|
|
14
|
+
protocol: process.env.PROTOCOL,
|
|
15
|
+
domain: process.env.HOST,
|
|
16
|
+
},
|
|
17
|
+
plugins: [
|
|
18
|
+
`gatsby-plugin-image`,
|
|
19
|
+
"gatsby-plugin-use-query-params",
|
|
20
|
+
"gatsby-plugin-svgr",
|
|
21
|
+
`gatsby-transformer-sharp`,
|
|
22
|
+
"gatsby-plugin-sass",
|
|
23
|
+
{
|
|
24
|
+
resolve: `gatsby-plugin-manifest`,
|
|
25
|
+
options: {
|
|
26
|
+
name: process.env.PROJECT_NAME,
|
|
27
|
+
short_name: process.env.PROJECT,
|
|
28
|
+
lang: process.env.PROJECT_DEFAULT_LANGUAGE,
|
|
29
|
+
start_url: `/`,
|
|
30
|
+
display: `standalone`,
|
|
31
|
+
icon: `static/icons/favicon.png`,
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
resolve: `gatsby-plugin-sharp`,
|
|
36
|
+
options: {
|
|
37
|
+
defaults: {
|
|
38
|
+
formats: [`auto`, `webp`, "avif"],
|
|
39
|
+
placeholder: `blurred`,
|
|
40
|
+
quality: 80,
|
|
41
|
+
breakpoints: [360, 769, 1024, 1216, 1408],
|
|
42
|
+
backgroundColor: `transparent`,
|
|
43
|
+
tracedSVGOptions: {},
|
|
44
|
+
blurredOptions: {},
|
|
45
|
+
jpgOptions: {},
|
|
46
|
+
pngOptions: {},
|
|
47
|
+
webpOptions: {},
|
|
48
|
+
avifOptions: {},
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
resolve: "gatsby-source-apiserver",
|
|
54
|
+
options: {
|
|
55
|
+
typePrefix: "API__",
|
|
56
|
+
method: "get",
|
|
57
|
+
allowCache: false,
|
|
58
|
+
headers: {
|
|
59
|
+
"Content-Type": "application/json",
|
|
60
|
+
},
|
|
61
|
+
data: {}, // Request body
|
|
62
|
+
params: {},
|
|
63
|
+
verboseOutput: false,
|
|
64
|
+
// enableDevRefresh: true,
|
|
65
|
+
entitiesArray: [
|
|
66
|
+
{
|
|
67
|
+
url: `${process.env.API_PROTOCOL}://${process.env.API_HOST}/v1/${process.env.PROJECT}/full-content/`,
|
|
68
|
+
headers: {
|
|
69
|
+
"Content-Type": "application/json",
|
|
70
|
+
},
|
|
71
|
+
name: "full-content",
|
|
72
|
+
skipCreateNode: true,
|
|
73
|
+
localSave: true,
|
|
74
|
+
path: `${process.env.GATSBY_PROJECT_ROOT}/src/data/pages/`,
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
url: `${process.env.API_PROTOCOL}://${process.env.API_HOST}/v1/${process.env.PROJECT}/redirection/`,
|
|
78
|
+
headers: {
|
|
79
|
+
"Content-Type": "application/json",
|
|
80
|
+
},
|
|
81
|
+
name: "redirections",
|
|
82
|
+
skipCreateNode: true,
|
|
83
|
+
localSave: true,
|
|
84
|
+
path: `${process.env.GATSBY_PROJECT_ROOT}/src/data/pages/`,
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
resolve: `gatsby-plugin-s3`,
|
|
91
|
+
options: {
|
|
92
|
+
bucketName: process.env.S3_BUCKET,
|
|
93
|
+
protocol: process.env.PROTOCOL,
|
|
94
|
+
hostname: process.env.HOST,
|
|
95
|
+
generateRoutingRules: true,
|
|
96
|
+
generateRedirectObjectsForPermanentRedirects: true,
|
|
97
|
+
acl: "private",
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
resolve: `gatsby-plugin-sitemap`,
|
|
102
|
+
options: {
|
|
103
|
+
excludes: ["/404/"],
|
|
104
|
+
query: `
|
|
105
|
+
{
|
|
106
|
+
allSitePage {
|
|
107
|
+
nodes {
|
|
108
|
+
path
|
|
109
|
+
pageContext
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
`,
|
|
114
|
+
resolveSiteUrl: () => `${process.env.PROTOCOL}://${process.env.HOST}`,
|
|
115
|
+
filterPages: (page: any) => page.is_hidden,
|
|
116
|
+
serialize: (page: any, { resolvePagePath }: any) => {
|
|
117
|
+
return {
|
|
118
|
+
url: `${resolvePagePath(page)}`,
|
|
119
|
+
lastmod: page.pageContext.published_at,
|
|
120
|
+
};
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
resolve: "gatsby-plugin-google-tagmanager",
|
|
126
|
+
options: {
|
|
127
|
+
id: process.env.GTM_ID,
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
resolve: "gatsby-plugin-newrelic",
|
|
132
|
+
options: {
|
|
133
|
+
config: {
|
|
134
|
+
instrumentationType: "proAndSPA",
|
|
135
|
+
accountId: process.env.NEWRELIC_ACCOUNT_ID,
|
|
136
|
+
trustKey: process.env.NEWRELIC_TRUST_KEY,
|
|
137
|
+
agentID: process.env.NEWRELIC_AGENT_ID,
|
|
138
|
+
licenseKey: process.env.NEWRELIC_LICENSE_KEY,
|
|
139
|
+
applicationID: process.env.NEWRELIC_APP_ID,
|
|
140
|
+
beacon: "bam.nr-data.net",
|
|
141
|
+
errorBeacon: "bam.nr-data.net",
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
...plugins,
|
|
146
|
+
],
|
|
147
|
+
...config,
|
|
148
|
+
};
|
|
149
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { NOptionsType } from "./types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
* Creates a Object with numeric options for a Select input.
|
|
6
|
+
*
|
|
7
|
+
* @param n Number of options to create
|
|
8
|
+
* @param start The number from which the options start
|
|
9
|
+
* @param defaultOpt Default options to be added into the resulting object
|
|
10
|
+
* @returns Object with the created options, for instance: { "0": "0", "1": "1", ... }
|
|
11
|
+
*
|
|
12
|
+
*/
|
|
13
|
+
export const nOptions = (
|
|
14
|
+
n: number,
|
|
15
|
+
start = 0,
|
|
16
|
+
defaultOpt = {}
|
|
17
|
+
): NOptionsType => {
|
|
18
|
+
const obj: NOptionsType = { ...defaultOpt };
|
|
19
|
+
for (let i = start; i < start + n; i++) {
|
|
20
|
+
obj[i.toString()] = i.toString();
|
|
21
|
+
}
|
|
22
|
+
return obj;
|
|
23
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
const slugify = module.require("slugify");
|
|
2
|
+
import { HotelOptionsByCityType, HotelPagesType, CityKeyType } from "./types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
* Returns Select options sorting by the cities with more hotels, otherwise alphabetically.
|
|
7
|
+
*
|
|
8
|
+
* For instance:
|
|
9
|
+
*
|
|
10
|
+
* [
|
|
11
|
+
* {
|
|
12
|
+
* label: 'Sydney',
|
|
13
|
+
* options: [
|
|
14
|
+
* { label: 'Vibe Hotel Sydney', value: "58443" },
|
|
15
|
+
* { label: 'Vibe Hotel Sydney Darling Harbour', value: "8565" }
|
|
16
|
+
* ]
|
|
17
|
+
* },
|
|
18
|
+
* {
|
|
19
|
+
* label: 'Mebourne',
|
|
20
|
+
* options: [
|
|
21
|
+
* { label: 'Vibe Hotel Melbourne', value: "9969" },
|
|
22
|
+
* ]
|
|
23
|
+
* },
|
|
24
|
+
* ]
|
|
25
|
+
*/
|
|
26
|
+
export const hotelOptionsByCity = (
|
|
27
|
+
hotelPages: HotelPagesType
|
|
28
|
+
): HotelOptionsByCityType[] => {
|
|
29
|
+
let hotelsByCity: Record<CityKeyType, { value: string; label: string }[]> = {
|
|
30
|
+
Sydney: [],
|
|
31
|
+
Melbourne: [],
|
|
32
|
+
Perth: [],
|
|
33
|
+
Hobart: [],
|
|
34
|
+
Canberra: [],
|
|
35
|
+
Adelaide: [],
|
|
36
|
+
"Gold Coast": [],
|
|
37
|
+
Darwin: [],
|
|
38
|
+
Singapore: [],
|
|
39
|
+
};
|
|
40
|
+
const propertyOrder = Object.entries(hotelPages).reduce(
|
|
41
|
+
(acc, [id, hotel]) => {
|
|
42
|
+
if (hotelsByCity[hotel.city]) {
|
|
43
|
+
hotelsByCity[hotel.city].push({ label: hotel.name, value: id });
|
|
44
|
+
acc[id] = hotel.sort;
|
|
45
|
+
}
|
|
46
|
+
return acc;
|
|
47
|
+
},
|
|
48
|
+
{} as { [k: string]: number }
|
|
49
|
+
);
|
|
50
|
+
return Object.entries(hotelsByCity).map(([city, options]) => {
|
|
51
|
+
return {
|
|
52
|
+
label: city,
|
|
53
|
+
options: options.sort(
|
|
54
|
+
(a, b) => propertyOrder[a.value] - propertyOrder[b.value]
|
|
55
|
+
),
|
|
56
|
+
};
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Joins the Property address
|
|
62
|
+
*/
|
|
63
|
+
export const joinAddress = (addressParams: (string | null)[]) =>
|
|
64
|
+
addressParams
|
|
65
|
+
.reduce((acc: string[], param: string | null) => {
|
|
66
|
+
if (param) {
|
|
67
|
+
acc.push(param);
|
|
68
|
+
}
|
|
69
|
+
return acc;
|
|
70
|
+
}, [])
|
|
71
|
+
.join(", ");
|
|
72
|
+
|
|
73
|
+
export const hotel_page_link = (city: string, hotelName: string) =>
|
|
74
|
+
`/book-accommodation/${slugify(city, {
|
|
75
|
+
lower: true,
|
|
76
|
+
})}/${slugify(hotelName, {
|
|
77
|
+
lower: true,
|
|
78
|
+
}).slice(5)}`;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Define a type for the city keys in `hotelsByCity`
|
|
2
|
+
export type CityKeyType =
|
|
3
|
+
| "Sydney"
|
|
4
|
+
| "Melbourne"
|
|
5
|
+
| "Perth"
|
|
6
|
+
| "Hobart"
|
|
7
|
+
| "Canberra"
|
|
8
|
+
| "Adelaide"
|
|
9
|
+
| "Gold Coast"
|
|
10
|
+
| "Darwin"
|
|
11
|
+
| "Singapore";
|
|
12
|
+
|
|
13
|
+
export interface HotelPagesType {
|
|
14
|
+
[hotel_code: string]: {
|
|
15
|
+
name: string;
|
|
16
|
+
city: CityKeyType;
|
|
17
|
+
sort: number;
|
|
18
|
+
link: string;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface HotelOptionsByCityType {
|
|
23
|
+
label: string;
|
|
24
|
+
options: { value: string; label: string }[];
|
|
25
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export const join_paths = (path1: string, path2: string): string =>
|
|
2
|
+
`${path1.replace(new RegExp(`/$`), "")}/${path2.replace(
|
|
3
|
+
new RegExp(`^/`),
|
|
4
|
+
""
|
|
5
|
+
)}`;
|
|
6
|
+
|
|
7
|
+
export const ssoURL = (path: string) =>
|
|
8
|
+
`${process.env.SABRE_SSO_PROTOCOL}://${process.env.SABRE_SSO_HOST}${path}`;
|