Skip to content

Commit

Permalink
Feature/onboarding (#59)
Browse files Browse the repository at this point in the history
* chore: add version number

* feat: basic implementation of settings form

* feat: basic templates

* feat: polished onboarding experience

* chore: remove log

* fix: more spacing

* chore: version bump

* fix: adjusted setting gaps
  • Loading branch information
noahstreller authored Jul 9, 2024
1 parent e808f86 commit 0a0cb0f
Show file tree
Hide file tree
Showing 12 changed files with 638 additions and 25 deletions.
2 changes: 2 additions & 0 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Footer } from "@/components/footer";
import HeaderComponent from "@/components/header";
import { LoadingScreen } from "@/components/loadingscreen";
import { Maintenance, MaintenanceType } from "@/components/pages/maintenance";
import { Onboarding } from "@/components/pages/onboarding";
import { ToasterWrapper } from "@/components/toaster-wrapper";
import { cn } from "@/lib/utils";
import type { Metadata, Viewport } from "next";
Expand Down Expand Up @@ -128,6 +129,7 @@ export default function RootLayout({
<Footer />
</div>
<LoadingScreen />
<Onboarding />
<ToasterWrapper />
</Providers>
)}
Expand Down
4 changes: 2 additions & 2 deletions components/footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ export function Footer() {
>
<GitBranch className="mr-2 size-4" />{" "}
{process.env.VERCEL_ENV === "production"
? "Production"
: process.env.VERCEL_GIT_COMMIT_REF || "Development"}
? process.env.npm_package_version || "Production"
: process.env.npm_package_version || "Development"}
</FooterItem>
</div>
</footer>
Expand Down
Empty file.
98 changes: 98 additions & 0 deletions components/pages/onboarding.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
"use client";
import { usePreferences } from "@/components/preferences-provider";
import { SettingsFormForOnboarding } from "@/components/settings-modal";
import { TemplateSelector } from "@/components/template-selector";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Highlight } from "@/components/ui/card-stack";
import { useDevice } from "@/lib/hooks/useMediaQuery";
import { PreferencesTranslations } from "@/lib/translationObjects";
import { cn } from "@/lib/utils";
import { motion } from "framer-motion";
import useTranslation from "next-translate/useTranslation";
import { useState } from "react";

export function Onboarding() {
const { t } = useTranslation("common");
const preferences = usePreferences();
const { isMobile, isTablet, isDesktop } = useDevice();

const preferencesTranslations: PreferencesTranslations = {
title: t("preferences.title"),
description: t("preferences.description"),
gradeDecimals: t("preferences.grade-decimals"),
gradeDecimalsDescription: t("preferences.grade-decimals-description"),
gradeDecimalsPlaceholder: t("preferences.grade-decimals-placeholder"),
keepModalsOpen: t("preferences.keep-modals-open"),
keepModalsOpenDescription: t("preferences.keep-modals-open-description"),
passingGrade: t("preferences.passing-grade"),
passingGradeDescription: t("preferences.passing-grade-description"),
passingGradePlaceholder: t("preferences.passing-grade-placeholder"),
minimumGrade: t("preferences.minimum-grade"),
minimumGradeDescription: t("preferences.minimum-grade-description"),
minimumGradePlaceholder: t("preferences.minimum-grade-placeholder"),
maximumGrade: t("preferences.maximum-grade"),
maximumGradeDescription: t("preferences.maximum-grade-description"),
maximumGradePlaceholder: t("preferences.maximum-grade-placeholder"),
passingInverse: t("preferences.passing-inverse"),
passingInverseDescription: t("preferences.passing-inverse-description"),
alertTitle: t("preferences.alert-title"),
alertDescription: t("preferences.alert-description"),
};

const [selectedTemplate, setSelectedTemplate] = useState<string>();

if (preferences.isDefault)
return (
<div className="fixed inset-0 flex flex-col items-center justify-start bg-background z-50 pt-8 overflow-scroll">
<div className="items-center gap-8 flex flex-col">
<h1 className="text-4xl font-bold tracking-tight text-white-900 sm:text-6xl">
<span className="text-muted-foreground">Grades</span>
<br />
Onboarding
</h1>
</div>
<div
className={cn(
"pt-12 flex items-start",
selectedTemplate && "gap-x-20 gap-y-12",
isMobile ? "flex-col" : "flex-row"
)}
>
<TemplateSelector setSelectedTemplate={setSelectedTemplate} />
<motion.div
layout
transition={{
type: "spring",
stiffness: 260,
damping: 20,
}}
>
{selectedTemplate && (
<Card className={cn(isMobile ? "max-w-80" : "max-w-md")}>
<CardHeader>
<CardTitle>Advanced Settings</CardTitle>
<CardDescription>
Hit <Highlight>Save</Highlight> to apply your changes and
complete the onboarding.
<br /> You can change this in the settings at any time.
</CardDescription>
</CardHeader>
<CardContent>
<SettingsFormForOnboarding
selectedTemplate={selectedTemplate || "percentage"}
translations={preferencesTranslations}
/>
</CardContent>
</Card>
)}
</motion.div>
</div>
</div>
);
}
23 changes: 17 additions & 6 deletions components/preferences-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,42 +6,53 @@ import { getDefaultPreferences } from "@/lib/utils";
import { useSession } from "next-auth/react";
import { createContext, useContext, useEffect, useState } from "react";


type PreferencesContextType = {
preferences: Preferences | undefined;
setPreferences: (preferences: Preferences) => void;
isDefault: boolean;
setIsDefault: (isDefault: boolean) => void;
loading: boolean;
};

const defaultContextValue: PreferencesContextType = {
preferences: undefined,
setPreferences: () => void 0,
isDefault: false,
setIsDefault: () => void 0,
loading: true,
};

const PreferencesContext = createContext(defaultContextValue);

export function PreferencesProvider({ children }: { children: React.ReactNode }) {
const [preferences, setPreferences] = useState<Preferences>(getDefaultPreferences());
export function PreferencesProvider({
children,
}: {
children: React.ReactNode;
}) {
const [preferences, setPreferences] = useState<Preferences>(
getDefaultPreferences()
);
const [isDefault, setIsDefault] = useState(false);
const [loading, setLoading] = useState(true);
const session = useSession();

useEffect(() => {
if (session.status === "authenticated") {
getPreferencesElseGetDefault().then((result): void => {
setPreferences(catchProblem(result));
setPreferences(catchProblem(result).preferences);
setIsDefault(catchProblem(result).isDefault);
setLoading(false);
});
}
}, [session]);

return (
<PreferencesContext.Provider
value={{ preferences, setPreferences, loading }}
value={{ preferences, setPreferences, loading, isDefault, setIsDefault }}
>
{children}
</PreferencesContext.Provider>
);
}

export const usePreferences = () => useContext(PreferencesContext);
export const usePreferences = () => useContext(PreferencesContext);
Loading

0 comments on commit 0a0cb0f

Please sign in to comment.