Skip to content

Commit

Permalink
Add dpa
Browse files Browse the repository at this point in the history
  • Loading branch information
lil5 committed Feb 14, 2024
1 parent af6af45 commit 46ac36e
Show file tree
Hide file tree
Showing 17 changed files with 1,635 additions and 133 deletions.
5 changes: 4 additions & 1 deletion frontend/public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -199,9 +199,12 @@
"mixedBagsUsuallyWorkBestThereforeWeRecommentTo": "Mixed bags usually work best, therefore we recommend to either tick all three boxes (X)S- M-(X)L for a mixed Loop, or to start a separate Loop for plus sizes.",
"termsOfService": "Terms of service",
"termsOfHosts": "Terms of hosts",
"dataProtectionAgreement": "Data protection agreement",
"acceptTohTitle": "Accept Terms of Hosts",
"acceptTohSubtitle": "As a host, you have to accept the Terms of Hosts to continue to organise your Loop(s)",
"acceptDpaTitle": "Accept Data Protection Agreement",
"acceptTohSubtitle": "As a host, you have to accept the Terms of Hosts and the Data Protection Agreement to continue to organise your Loop(s)",
"youMustScrollToAcceptToh": "You must have scrolled to the bottom to accept or deny the Terms of Hosts.",
"youMustScrollToAcceptDpa": "You must have scrolled to the bottom to accept or deny the Data Protection Agreement.",
"ifClickDenyTohSetHost": "If you click “Deny” you will be set as a participant on all your Loops.",
"privacy": "Privacy",
"iAmNotAMinor<1>Tos</1>And<2>PrivacyPolicy</2>Star": "I am not a minor and accept and agree to the <1>Terms of Use</1> and <2>Privacy Policy</2>*",
Expand Down
8 changes: 8 additions & 0 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ const About = React.lazy(() => import("./pages/About"));
const PrivacyPolicy = React.lazy(() => import("./pages/PrivacyPolicy"));
const TermsOfUse = React.lazy(() => import("./pages/TermsOfUse"));
const TermsOfHosts = React.lazy(() => import("./pages/TermsOfHosts"));
const DataProtectionAgreement = React.lazy(
() => import("./pages/DataProtectionAgreement")
);
const FAQ = React.lazy(() => import("./pages/FAQ"));
const AdminDashboard = React.lazy(() => import("./pages/AdminDashboard"));
const Events = React.lazy(() => import("./pages/Events"));
Expand Down Expand Up @@ -209,6 +212,11 @@ export default function App() {
path={`${base}/terms-of-hosts`}
component={TermsOfHosts}
/>
<Route
exact
path={`${base}/data-protection-agreement`}
component={DataProtectionAgreement}
/>
<Route
exact
path={`${base}/privacy-policy`}
Expand Down
1 change: 1 addition & 0 deletions frontend/src/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export interface User {
longitude?: number;
latitude?: number;
accepted_toh?: boolean;
accepted_dpa?: boolean;
}

export interface Bag {
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/api/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export interface UserUpdateBody {
i18n?: string;
longitude?: number;
latitude?: number;
accepted_toh?: boolean;
accepted_legal?: boolean;
}
export function userUpdate(user: UserUpdateBody) {
return window.axios.patch<never>("/v2/user", user);
Expand Down
29 changes: 29 additions & 0 deletions frontend/src/components/ChainDetailsForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ interface Props {
initialValues?: RegisterChainForm;
showBack?: boolean;
showAllowedTOH: boolean;
showAllowedDPA: boolean;
}

export type RegisterChainForm = Omit<
Expand Down Expand Up @@ -71,6 +72,7 @@ export default function ChainDetailsForm({
initialValues,
showBack,
showAllowedTOH,
showAllowedDPA,
}: Props) {
const { t } = useTranslation();
const { addToastError } = useContext(ToastContext);
Expand Down Expand Up @@ -306,6 +308,33 @@ export default function ChainDetailsForm({
</div>
) : null}

{showAllowedDPA ? (
<div className="form-control">
<label className="label cursor-pointer">
<span className="label-text">
<Trans
i18nKey="iAccept<1>Dpa</1>Star"
components={{
"1": (
<a
href="/data-protection-agreement"
target="_blank"
className="link"
></a>
),
}}
></Trans>
</span>
<input
type="checkbox"
required
className="checkbox border-black"
name="toh"
/>
</label>
</div>
) : null}

<div className="flex flex-col sm:flex-row items-end mb-6">
<div className="w-full sm:w-1/2 pb-4 sm:pb-0 sm:pr-4 rtl:sm:pr-0 rtl:sm:pl-4">
<CategoriesDropdown
Expand Down
8 changes: 7 additions & 1 deletion frontend/src/components/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ export default function Footer() {

<div className="bg-teal text-white">
<div className="container mx-auto px-1 md:px-20 py-4 flex flex-col md:flex-row justify-between items-center">
<div className="flex flex-col sm:flex-row sm:flex-wrap mb-2 md:mb-0">
<div className="flex flex-col md:flex-row sm:flex-wrap mb-2 md:mb-0">
<Link
className="btn btn-ghost text-white text-base font-normal"
to="/terms-of-use"
Expand All @@ -220,6 +220,12 @@ export default function Footer() {
>
{t("termsOfHosts")}
</Link>
<Link
className="btn btn-ghost text-white text-base font-normal"
to="/data-protection-agreement"
>
{t("dataProtectionAgreement")}
</Link>
<Link
className="btn btn-ghost text-white text-base font-normal"
to="/privacy-policy"
Expand Down
179 changes: 179 additions & 0 deletions frontend/src/components/PopupLegal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
import { TFunction } from "i18next";
import { useRef, UIEvent, useEffect, useState, RefObject } from "react";
import { useDebouncedCallback } from "use-debounce";
import { TermsOfHostsHTML } from "../pages/TermsOfHosts";
import { userUpdate } from "../api/user";
import { UserRefreshState } from "../providers/AuthProvider";
import { Modal } from "../providers/ToastProvider";
import { User } from "../api/types";
import { DataProtectionAgreementHTML } from "../pages/DataProtectionAgreement";

interface Props {
t: TFunction;
authUserRefresh: () => Promise<UserRefreshState>;
addModal: (t: Modal) => void;
authUser: User;
tmpAcceptedToh: boolean;
setTmpAcceptedToh: (s: boolean) => void;
}

export default function PopupLegal({
t,
authUserRefresh,
addModal,
authUser,
tmpAcceptedToh,
setTmpAcceptedToh,
}: Props) {
return addModal({
message: "",
content: () => {
const refTohScroll = useRef<HTMLDivElement>(null);
const refRoot = useRef<HTMLDivElement>(null);
const refDpaScroll = useRef<HTMLDivElement>(null);
const getElBtn = () =>
refRoot.current?.parentElement?.querySelectorAll(
"&>div:nth-child(3) > button"
) as NodeListOf<HTMLButtonElement>;
const scrollingCheck = useDebouncedCallback(
(e: UIEvent<HTMLDivElement>) => {
let target = e.target as HTMLDivElement;
// if scrolled to the bottom of the page
if (
target.scrollTop + target.clientHeight + 200 >
target.scrollHeight
) {
getElBtn().forEach((el) => el.removeAttribute("disabled"));
}
},
300,
{
trailing: true,
}
);

useEffect(() => {
getElBtn().forEach((el) => el.setAttribute("disabled", "disabled"));
}, []);

const scrollDown = (ref: RefObject<HTMLDivElement>) => {
ref.current?.scrollTo({
top: ref.current!.scrollHeight,
left: 0,
behavior: "smooth",
});
};
const scrollDownToh = () => scrollDown(refTohScroll);
const scrollDownDpa = () => scrollDown(refDpaScroll);

let showToh = !(authUser.accepted_toh || tmpAcceptedToh);
let showDpa = showToh ? false : authUser.accepted_dpa === false;
return (
<div ref={refRoot}>
{showToh ? (
<>
<h1 className="-mt-6 mb-4">{t("acceptTohTitle")}</h1>
<p className="mb-4 text-sm">{t("acceptTohSubtitle")}</p>

<div className="relative">
<div
ref={refTohScroll}
className="border border-grey overflow-y-auto h-[33.333vh] text-xs py-0.5 px-2 bg-grey-light"
onScroll={scrollingCheck}
>
<TermsOfHostsHTML className="prose text-xs prose-terms-modal" />
</div>
<button
onClick={scrollDownToh}
className="absolute bottom-2 ltr:right-2 rtl:left-2 btn btn-circle btn-sm btn-secondary text-white opacity-50 hover:opacity-90 tooltip ltr:tooltip-left rtl:tooltip-right before:font-normal before:text-sm"
data-tip="Scroll to the bottom."
>
<span className="feather feather-arrow-down font-bold" />
</button>
</div>
</>
) : null}
{showDpa ? (
<>
<h1 className="-mt-6 mb-4">{t("acceptDpaTitle")}</h1>
<p className="mb-4 text-sm">{t("acceptTohSubtitle")}</p>
<div className="relative">
<div
ref={refDpaScroll}
className="border border-grey overflow-y-auto h-[33.333vh] text-xs py-0.5 px-2 bg-grey-light"
onScroll={scrollingCheck}
>
<DataProtectionAgreementHTML
authUser={authUser}
className="prose text-xs prose-terms-modal"
/>
</div>
<button
onClick={scrollDownDpa}
className="absolute bottom-2 ltr:right-2 rtl:left-2 btn btn-circle btn-sm btn-secondary text-white opacity-50 hover:opacity-90 tooltip ltr:tooltip-left rtl:tooltip-right before:font-normal before:text-sm"
data-tip="Scroll to the bottom."
>
<span className="feather feather-arrow-down font-bold" />
</button>
</div>
</>
) : null}
<p className="text-xs mt-3 leading-relaxed">
{showToh
? t("youMustScrollToAcceptToh")
: t("youMustScrollToAcceptDpa")}
<br />
<span className="text-red font-semibold">
{t("ifClickDenyTohSetHost")}
</span>
</p>
</div>
);
},
actions: [
{
type: "primary",
text: t("accept"),
fn: () => {
if (
authUser.accepted_toh === false &&
tmpAcceptedToh === false &&
authUser.accepted_dpa === false
) {
setTmpAcceptedToh(true);
authUserRefresh();
return;
}
userUpdate({
user_uid: authUser.uid,
accepted_legal: true,
}).then(() => {
setTmpAcceptedToh(true);
authUserRefresh();
});
},
submit: true,
},
{
type: "error",
text: t("deny"),
fn: () => {
if (
confirm(
t("areYouSureRevokeHost", { name: authUser.name }) as string
)
) {
userUpdate({
user_uid: authUser.uid,
accepted_legal: false,
}).then(() => {
authUserRefresh();
});
}
},
submit: true,
},
],
forceOpen: true,
});
}
Loading

0 comments on commit 46ac36e

Please sign in to comment.