mirror of
https://github.com/frontendnetwork/vegancheck.me
synced 2024-09-20 22:11:59 +00:00
fix(AAA): #507
This commit is contained in:
parent
375c4a212b
commit
12907e5b46
17 changed files with 112 additions and 85 deletions
|
@ -178,13 +178,16 @@ const ProductSearch = () => {
|
|||
>
|
||||
<legend>{t("enterbarcode")}</legend>
|
||||
<fieldset>
|
||||
<legend>{t("enterbarcode")}</legend>
|
||||
<Scan
|
||||
onDetected={(barcode) => setBarcode(barcode)}
|
||||
handleSubmit={(barcode) => handleSubmit(barcode)}
|
||||
/>
|
||||
<label htmlFor="barcodeInput" className="hidden">{t("enterbarcode")}</label>
|
||||
<input
|
||||
type="number"
|
||||
name="barcode"
|
||||
id="barcodeInput"
|
||||
placeholder={t("enterbarcode")}
|
||||
autoFocus={true}
|
||||
value={barcode}
|
||||
|
|
|
@ -2,7 +2,7 @@ import Image from "next/image";
|
|||
import { useTranslations } from "next-intl";
|
||||
import { useState } from "react";
|
||||
|
||||
const SupportOption: React.FC = () => {
|
||||
const SupportOption = () => {
|
||||
const t = useTranslations("More");
|
||||
const [icon, setIcon] = useState<string>("icon-paypal");
|
||||
const [vendor, setVendor] = useState<string>("PayPal");
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
import { useTranslations } from 'next-intl';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
const OLEDMode: React.FC = () => {
|
||||
const t = useTranslations('More');
|
||||
const OLEDMode = () => {
|
||||
const t = useTranslations("More");
|
||||
const [isChecked, setIsChecked] = useState<boolean>(false);
|
||||
const [error, setError] = useState<boolean>(false);
|
||||
|
||||
const setThemeColorAttribute = (color: string) => {
|
||||
const themeColorElement = document.querySelector<HTMLMetaElement>('meta[name="theme-color"][media="(prefers-color-scheme: dark)"]');
|
||||
const themeColorElement = document.querySelector<HTMLMetaElement>(
|
||||
'meta[name="theme-color"][media="(prefers-color-scheme: dark)"]'
|
||||
);
|
||||
|
||||
if (themeColorElement) {
|
||||
themeColorElement.setAttribute("content", color);
|
||||
|
@ -16,7 +18,10 @@ const OLEDMode: React.FC = () => {
|
|||
|
||||
useEffect(() => {
|
||||
const localStorageValue = localStorage.getItem("oled");
|
||||
if (localStorageValue === "true" && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
||||
if (
|
||||
localStorageValue === "true" &&
|
||||
window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||
) {
|
||||
document.documentElement.setAttribute("data-theme", "oled");
|
||||
setThemeColorAttribute("#000");
|
||||
setIsChecked(true);
|
||||
|
@ -24,14 +29,17 @@ const OLEDMode: React.FC = () => {
|
|||
}, []);
|
||||
|
||||
const handleClick = () => {
|
||||
if (!isChecked && !window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
||||
if (
|
||||
!isChecked &&
|
||||
!window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||
) {
|
||||
setError(true);
|
||||
return;
|
||||
}
|
||||
if (!isChecked) {
|
||||
document.documentElement.setAttribute("data-theme", "oled");
|
||||
setThemeColorAttribute("#000");
|
||||
localStorage.setItem('oled', 'true');
|
||||
localStorage.setItem("oled", "true");
|
||||
} else {
|
||||
localStorage.clear();
|
||||
document.documentElement.removeAttribute("data-theme");
|
||||
|
@ -42,11 +50,19 @@ const OLEDMode: React.FC = () => {
|
|||
};
|
||||
|
||||
return (
|
||||
<span className="Grid switcher">
|
||||
<label htmlFor="oled-switch" className="Grid switcher">
|
||||
<div className="Grid-cell description">
|
||||
OLED-Mode
|
||||
<span className="info" id="cookieinfo">{t('thissetsacookie')}</span>
|
||||
<span className={`info ${error ? "animated fadeIn" : ""}`} id="oledinfo" style={{display: error ? "block" : "none"}}>{t('activatedarkmode')}</span>
|
||||
<span className="info" id="cookieinfo">
|
||||
{t("thissetsacookie")}
|
||||
</span>
|
||||
<span
|
||||
className={`info ${error ? "animated fadeIn" : ""}`}
|
||||
id="oledinfo"
|
||||
style={{ display: error ? "block" : "none" }}
|
||||
>
|
||||
{t("activatedarkmode")}
|
||||
</span>
|
||||
</div>
|
||||
<div className="Grid-cell icons">
|
||||
<input
|
||||
|
@ -58,7 +74,7 @@ const OLEDMode: React.FC = () => {
|
|||
onChange={handleClick}
|
||||
/>
|
||||
</div>
|
||||
</span>
|
||||
</label>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -9,9 +9,18 @@ interface ModalProps {
|
|||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const ModalWrapper: React.FC<ModalProps> = ({ children, id, buttonType, buttonClass, buttonText }) => {
|
||||
const ModalWrapper = ({
|
||||
children,
|
||||
id,
|
||||
buttonType,
|
||||
buttonClass,
|
||||
buttonText,
|
||||
}: ModalProps) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const modalRoot = typeof document !== 'undefined' ? document.getElementById("modal-root") : null;
|
||||
const modalRoot =
|
||||
typeof document !== "undefined"
|
||||
? document.getElementById("modal-root")
|
||||
: null;
|
||||
|
||||
useEffect(() => {
|
||||
const handleEscapeKeyPress = (event: KeyboardEvent) => {
|
||||
|
@ -35,7 +44,6 @@ const ModalWrapper: React.FC<ModalProps> = ({ children, id, buttonType, buttonCl
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
document.addEventListener("keydown", handleEscapeKeyPress);
|
||||
document.addEventListener("touchstart", handleTouchStart);
|
||||
|
||||
|
@ -91,15 +99,12 @@ const ModalWrapper: React.FC<ModalProps> = ({ children, id, buttonType, buttonCl
|
|||
{buttonText}
|
||||
</div>
|
||||
)}
|
||||
{isOpen && modalRoot &&
|
||||
{isOpen &&
|
||||
modalRoot &&
|
||||
createPortal(
|
||||
<div className="modal_view animated fadeInUp open">
|
||||
<div className="modal_close">
|
||||
<a
|
||||
className="btn-dark"
|
||||
data-dismiss="modal"
|
||||
onClick={closeModal}
|
||||
>
|
||||
<a className="btn-dark" data-dismiss="modal" onClick={closeModal}>
|
||||
×
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
@ -9,7 +9,7 @@ interface ShareButtonProps {
|
|||
barcode: string;
|
||||
}
|
||||
|
||||
const ShareButton: React.FC<ShareButtonProps> = ({ productName, barcode }) => {
|
||||
const ShareButton = ({ productName, barcode }: ShareButtonProps) => {
|
||||
const t = useTranslations("Check");
|
||||
const [showButton, setShowButton] = useState<boolean>(false);
|
||||
|
||||
|
@ -91,7 +91,9 @@ const ShareButton: React.FC<ShareButtonProps> = ({ productName, barcode }) => {
|
|||
text,
|
||||
url,
|
||||
})
|
||||
.catch((err) => {console.error(err)});
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t("share")}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import Image from "next/image";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useState, useEffect, FC } from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
interface ExtendedWindow extends Window {
|
||||
MSStream?: any;
|
||||
}
|
||||
|
||||
const Shortcut: FC = () => {
|
||||
const Shortcut = () => {
|
||||
const t = useTranslations("ShortcutPrompt");
|
||||
const [showShortcut, setShowShortcut] = useState<boolean>(false);
|
||||
|
||||
|
@ -16,7 +16,8 @@ const Shortcut: FC = () => {
|
|||
const windowWithMSStream = window as ExtendedWindow;
|
||||
|
||||
const isIOS: boolean =
|
||||
/iPad|iPhone|iPod/.test(navigator.userAgent) && !windowWithMSStream.MSStream;
|
||||
/iPad|iPhone|iPod/.test(navigator.userAgent) &&
|
||||
!windowWithMSStream.MSStream;
|
||||
|
||||
if (
|
||||
!window.matchMedia("(display-mode: standalone)").matches &&
|
||||
|
|
|
@ -5,11 +5,6 @@ import React, { useState, FormEvent } from "react";
|
|||
|
||||
import ModalWrapper from "@/components/elements/modalwrapper";
|
||||
|
||||
export interface FlaggedItem {
|
||||
item: string;
|
||||
index: number;
|
||||
}
|
||||
|
||||
const IngredientsCheck = () => {
|
||||
const t = useTranslations("Ingredients");
|
||||
const [flagged, setFlagged] = useState<string[]>([]);
|
||||
|
|
4
src/models/FlaggedItem.ts
Normal file
4
src/models/FlaggedItem.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
export type FlaggedItem = {
|
||||
item: string;
|
||||
index: number;
|
||||
};
|
|
@ -1,17 +1,27 @@
|
|||
import { GetStaticPropsContext } from 'next'
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { GetStaticPropsContext } from "next";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
import Container from "@/components/elements/container";
|
||||
import Nav from "@/components/nav";
|
||||
|
||||
export default function NotFound() {
|
||||
const t = useTranslations('404');
|
||||
const t = useTranslations("404");
|
||||
return (
|
||||
<>
|
||||
<Nav />
|
||||
<Container heading={t('error404')}>
|
||||
<p>{t('pagedoesnotexist')}</p>
|
||||
<p dangerouslySetInnerHTML={{ __html: t('message', {statuspage: `<a href="https://stats.uptimerobot.com/LY1gRuP5j6/789004495">${t('statuspage')}</a>`, mastodon: '<a href="https://veganism.social/@vegancheck">Mastodon</a>'})}} />
|
||||
<Container heading={t("error404")}>
|
||||
<p>{t("pagedoesnotexist")}</p>
|
||||
<p
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: t("message", {
|
||||
statuspage: `<a href="https://stats.uptimerobot.com/LY1gRuP5j6/789004495">${t(
|
||||
"statuspage"
|
||||
)}</a>`,
|
||||
mastodon:
|
||||
'<a href="https://veganism.social/@vegancheck">Mastodon</a>',
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
</Container>
|
||||
</>
|
||||
);
|
||||
|
@ -20,7 +30,7 @@ export default function NotFound() {
|
|||
export async function getStaticProps({ locale }: GetStaticPropsContext) {
|
||||
return {
|
||||
props: {
|
||||
messages: (await import(`../locales/${locale}.json`)).default
|
||||
}
|
||||
messages: (await import(`../locales/${locale}.json`)).default,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,13 +1,8 @@
|
|||
import "@/styles/style.scss";
|
||||
import { init } from "@socialgouv/matomo-next";
|
||||
import type { AppProps } from "next/app";
|
||||
import { NextIntlProvider } from "next-intl";
|
||||
import { useEffect } from "react";
|
||||
|
||||
export default function App({ Component, pageProps }: AppProps) {
|
||||
useEffect(() => {
|
||||
init({ url: "https://analytics.vegancheck.me", siteId: "1" });
|
||||
}, []);
|
||||
return (
|
||||
<>
|
||||
<NextIntlProvider messages={pageProps.messages}>
|
||||
|
|
|
@ -1,30 +1,28 @@
|
|||
import { GetStaticPropsContext } from 'next'
|
||||
import { useTranslations } from 'next-intl';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { GetStaticPropsContext } from "next";
|
||||
import { useTranslations } from "next-intl";
|
||||
import React, { useState, useEffect } from "react";
|
||||
|
||||
import Container from "@/components/elements/container";
|
||||
import Nav from "@/components/nav";
|
||||
|
||||
const Impressum = () => {
|
||||
const t = useTranslations();
|
||||
const [impressum, setImpressum] = useState('');
|
||||
const [impressum, setImpressum] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
fetch('https://philipbrembeck.com/impressum.txt', {method: "GET"})
|
||||
.then(response => response.text())
|
||||
.then(text => setImpressum(text))
|
||||
.catch(error => console.error(error));
|
||||
fetch("https://philipbrembeck.com/impressum.txt", { method: "GET" })
|
||||
.then((response) => response.text())
|
||||
.then((text) => setImpressum(text))
|
||||
.catch((error) => console.error(error));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Nav />
|
||||
<Container heading={t('More.imprint')}>
|
||||
<p className="small">
|
||||
{t('Privacy.germanonly')}
|
||||
</p>
|
||||
<div dangerouslySetInnerHTML={{ __html: impressum }} />
|
||||
</Container>
|
||||
<Nav />
|
||||
<Container heading={t("More.imprint")}>
|
||||
<p className="small">{t("Privacy.germanonly")}</p>
|
||||
<div dangerouslySetInnerHTML={{ __html: impressum }} />
|
||||
</Container>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -34,7 +32,7 @@ export default Impressum;
|
|||
export async function getStaticProps({ locale }: GetStaticPropsContext) {
|
||||
return {
|
||||
props: {
|
||||
messages: (await import(`../locales/${locale}.json`)).default
|
||||
}
|
||||
messages: (await import(`../locales/${locale}.json`)).default,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { GetStaticPropsContext } from "next";
|
||||
|
||||
import Container from "@/components/elements/container";
|
||||
import IngredientsCheck from '@/components/ingredientscheck'
|
||||
import IngredientsCheck from "@/components/ingredientscheck";
|
||||
import Nav from "@/components/nav";
|
||||
|
||||
export default function ingredients() {
|
||||
|
@ -9,10 +9,7 @@ export default function ingredients() {
|
|||
<>
|
||||
<div id="modal-root"></div>
|
||||
<Nav />
|
||||
<Container
|
||||
logo={false}
|
||||
backbutton={false}
|
||||
>
|
||||
<Container logo={false} backbutton={false}>
|
||||
<IngredientsCheck />
|
||||
</Container>
|
||||
</>
|
||||
|
|
|
@ -108,7 +108,10 @@ export default function More() {
|
|||
<span className="unknown icon-right-open"></span>
|
||||
</div>
|
||||
</Link>
|
||||
<a href="https://frontendnet.work/vegancheck-api" className="Grid links">
|
||||
<a
|
||||
href="https://frontendnet.work/vegancheck-api"
|
||||
className="Grid links"
|
||||
>
|
||||
<div className="Grid-cell description">{t("apidocumentation")}</div>
|
||||
<div className="Grid-cell icons">
|
||||
<span className="unknown icon-right-open"></span>
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
import { GetStaticPropsContext } from 'next'
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { GetStaticPropsContext } from "next";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
import Container from "@/components/elements/container";
|
||||
import Nav from "@/components/nav";
|
||||
|
||||
export default function TOS() {
|
||||
const t = useTranslations('TOS');
|
||||
const t = useTranslations("TOS");
|
||||
return (
|
||||
<>
|
||||
<Nav />
|
||||
<Container heading={t('tos')}>
|
||||
<p className="small">
|
||||
{t('englishgermanonly')}
|
||||
</p>
|
||||
<div dangerouslySetInnerHTML={{__html: t.raw('tos_content')}} />
|
||||
<Container heading={t("tos")}>
|
||||
<p className="small">{t("englishgermanonly")}</p>
|
||||
<div dangerouslySetInnerHTML={{ __html: t.raw("tos_content") }} />
|
||||
</Container>
|
||||
</>
|
||||
);
|
||||
|
@ -22,7 +20,7 @@ export default function TOS() {
|
|||
export async function getStaticProps({ locale }: GetStaticPropsContext) {
|
||||
return {
|
||||
props: {
|
||||
messages: (await import(`../locales/${locale}.json`)).default
|
||||
}
|
||||
messages: (await import(`../locales/${locale}.json`)).default,
|
||||
},
|
||||
};
|
||||
}
|
|
@ -1,3 +1,3 @@
|
|||
/* Do use: Normalize.css as a CSS reset */
|
||||
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
|
||||
button,hr,input{overflow:visible}progress,sub,sup{vertical-align:baseline}[type=checkbox],[type=radio],legend{box-sizing:border-box;padding:0}html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}details,main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0}code,kbd,pre,samp{font-family:monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:ButtonText dotted 1px}fieldset{padding:.35em .75em .625em}legend{color:inherit;display:table;max-width:100%;white-space:normal}textarea{overflow:auto}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}[hidden],template{display:none}
|
||||
button,hr,input{overflow:visible}progress,sub,sup{vertical-align:baseline}[type=checkbox],[type=radio],legend{box-sizing:border-box;padding:0}html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}details,main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0}code,kbd,pre,samp{font-family:monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:ButtonText dotted 1px}fieldset{padding:.35em .75em .625em}legend{color:inherit;display:table;max-width:100%;white-space:normal}textarea{overflow:auto}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}[hidden],template{display:none}.hidden{display:none;}
|
|
@ -118,7 +118,7 @@
|
|||
display: block;
|
||||
font-weight: 400;
|
||||
font-size: rem(14.4px);
|
||||
color: #ccc;
|
||||
color: #595959;
|
||||
margin-top: 0;
|
||||
padding: 0;
|
||||
line-height: rem(16px);
|
||||
|
|
Loading…
Reference in a new issue