Skip to content

Commit

Permalink
#17 admin routes and login with Slack
Browse files Browse the repository at this point in the history
  • Loading branch information
ilbertt committed May 31, 2022
1 parent 57bf4c5 commit 5cfc757
Show file tree
Hide file tree
Showing 13 changed files with 399 additions and 173 deletions.
37 changes: 6 additions & 31 deletions apps/sumilan-app/src/app/App.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { IonApp, IonRouterOutlet, IonSplitPane } from '@ionic/react';
import { IonApp } from '@ionic/react';
import { IonReactRouter } from '@ionic/react-router';
import { Redirect, Route } from 'react-router-dom';
import i18n from "i18next";
import LanguageDetector from 'i18next-browser-languagedetector';
import { initReactI18next } from "react-i18next";
Expand All @@ -24,18 +23,12 @@ import '@ionic/react/css/display.css';
/* Theme variables */
import './theme/variables.css';

import Menu from './components/Menu';
import Home from './pages/Home/Home';
import Event from './pages/Event/Event';
import Young from './pages/Young/Young';
import Chapter from './pages/Chapter/Chapter';
import Activist from './pages/Activist/Activits';
import Certificates from './pages/Certificates/Certificates';

import en from '../assets/i18n/en.json';
import it from '../assets/i18n/it.json';
import { getAppVersion } from './utils/version';
import { loadGA } from './libs/ga';
import Routes from './pages/Routes';
import { AuthProvider } from './contexts/Auth';

i18n
.use(LanguageDetector)
Expand Down Expand Up @@ -64,30 +57,12 @@ loadGA(process.env['NX_GOOGLE_ANALYTICS_ID'] as string);

const App: React.FC = () => {

const homeRouter = (
<IonSplitPane contentId="main">
<Menu />
<IonRouterOutlet id="main">
<Route path="/" exact>
<Redirect to="/home" />
</Route>
<Route path="/start" exact>
<Redirect to="/home" />
</Route>
<Route path="/home" exact={true} component={Home} />
{/* <Route path="/young" exact={true} component={Young} /> */}
<Route path="/chapter" exact={true} component={Chapter} />
<Route path="/activist" exact={true} component={Activist} />
<Route path="/certificates" exact={true} component={Certificates} />
<Route path="/event/:id/" render={(props) => <Event {...props} />} />
</IonRouterOutlet>
</IonSplitPane>
);

return (
<IonApp>
<IonReactRouter>
{homeRouter}
<AuthProvider>
<Routes />
</AuthProvider>
</IonReactRouter>
</IonApp>
);
Expand Down
113 changes: 113 additions & 0 deletions apps/sumilan-app/src/app/components/Admin/AdminMenu.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
ion-menu ion-content {
--background: var(--ion-item-background, var(--ion-background-color, #fff));
}

ion-menu.md ion-content {
--padding-start: 8px;
--padding-end: 8px;
--padding-top: 20px;
--padding-bottom: 20px;
}

ion-menu.md ion-list {
padding: 20px 0;
}

ion-menu.md ion-note {
margin-bottom: 30px;
}

ion-menu.md ion-list-header, ion-menu.md ion-note {
padding-left: 10px;
}

ion-menu.md ion-list#inbox-list {
border-bottom: 1px solid var(--ion-color-step-150, #d7d8da);
}

ion-menu.md ion-list#inbox-list ion-list-header {
font-size: 22px;
font-weight: 600;
min-height: 20px;
}

ion-menu.md ion-list#labels-list ion-list-header {
font-size: 16px;
margin-bottom: 18px;
color: #757575;
min-height: 26px;
}

ion-menu.md ion-item {
--padding-start: 10px;
--padding-end: 10px;
border-radius: 4px;
}

ion-menu.md ion-item.selected {
--background: rgba(var(--ion-color-primary-rgb), 0.14);
}

ion-menu.md ion-item.selected ion-icon {
color: var(--ion-color-primary);
}

ion-menu.md ion-item ion-icon {
color: #616e7e;
}

ion-menu.md ion-item ion-label {
font-weight: 500;
}

ion-menu.ios ion-content {
--padding-bottom: 20px;
}

ion-menu.ios ion-list {
padding: 20px 0 0 0;
}

ion-menu.ios ion-note {
line-height: 24px;
margin-bottom: 20px;
}

ion-menu.ios ion-item {
--padding-start: 16px;
--padding-end: 16px;
--min-height: 50px;
}

ion-menu.ios ion-item ion-icon {
font-size: 24px;
color: #73849a;
}

ion-menu.ios ion-item .selected ion-icon {
color: var(--ion-color-primary);
}

ion-menu.ios ion-list#labels-list ion-list-header {
margin-bottom: 8px;
}

ion-menu.ios ion-list-header,
ion-menu.ios ion-note {
padding-left: 16px;
padding-right: 16px;
}

ion-menu.ios ion-note {
margin-bottom: 8px;
}

ion-note {
display: inline-block;
font-size: 16px;
color: var(--ion-color-medium-shade);
}

ion-item.selected {
--color: var(--ion-color-primary);
}
105 changes: 105 additions & 0 deletions apps/sumilan-app/src/app/components/Admin/AdminMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { useRef } from 'react';
import {
IonAvatar,
IonButton,
IonContent,
IonFooter,
IonIcon,
IonItem,
IonLabel,
IonList,
IonMenu,
IonMenuToggle,
} from '@ionic/react';
import { useLocation } from 'react-router-dom';
import { help, logOutOutline } from 'ionicons/icons';
import { useTranslation } from 'react-i18next';

import { openPreferenceCenter } from '../../libs/avacy';
import { getAppVersion } from '../../utils/version';
import { useAuth } from '../../contexts/Auth';
import { AuthContextModel } from '../../models/auth.model';

import './AdminMenu.css';

interface AppPage {
url: string;
iosIcon: string;
mdIcon: string;
}

const appPages: AppPage[] = [
{
url: 'questions',
iosIcon: help,
mdIcon: help
},
];

const AdminMenu: React.FC = () => {
const menuRef = useRef<HTMLIonMenuElement>(null);
const location = useLocation();
const { user, signOut } = useAuth() as AuthContextModel;
const { t } = useTranslation();

const handleCookieClick = () => {
openPreferenceCenter();
menuRef.current?.close();
};

const handleLogoutClick = () => {
const wantsToLogout = window.confirm("Do you want to logout?");
if (wantsToLogout) {
signOut();
}
};

return (
<IonMenu contentId="main" type="overlay" ref={menuRef}>
<IonContent>
<IonList id="inbox-list">

{user && (
<IonItem>
<IonAvatar slot="start">
<img src={user.user_metadata['avatar_url']} alt="Profile avatar" />
</IonAvatar>
<IonLabel>{user.user_metadata['name']}</IonLabel>
<IonButton slot="end" fill="clear" onClick={handleLogoutClick}>
<IonIcon slot="icon-only" icon={logOutOutline} />
</IonButton>
</IonItem>
)}

{appPages.map((appPage, index) => {
return (
<IonMenuToggle key={index} autoHide={false}>
<IonItem className={location.pathname === appPage.url ? 'selected' : ''} routerLink={`${location.pathname}/${appPage.url}`} routerDirection="none" lines="none" detail={false}>
<IonIcon slot="start" ios={appPage.iosIcon} md={appPage.mdIcon} />
<IonLabel>{t(appPage.url.toUpperCase() + '.title')}</IonLabel>
</IonItem>
</IonMenuToggle>
);
})}

</IonList>
</IonContent>
<IonFooter>
<IonList style={{ padding: 0 }}>
<IonItem lines="none" button onClick={handleCookieClick}>
<IonLabel style={{ margin: 0 }}>
<p>{t("COOKIES.preferences")}</p>
</IonLabel>
</IonItem>
<IonItem lines="none">
<IonLabel style={{ margin: 0 }}>
<p>Version: {getAppVersion()}</p>
</IonLabel>
</IonItem>
</IonList>
</IonFooter>
</IonMenu>
);
};

export default AdminMenu;
4 changes: 3 additions & 1 deletion apps/sumilan-app/src/app/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,6 @@ export const PROGRESS_STEPS = [
id: 5,
progress: 1,
},
];
];

export const ADMIN_DASHBOARD_BASE_PATH = '/admin';
47 changes: 26 additions & 21 deletions apps/sumilan-app/src/app/contexts/Auth.tsx
Original file line number Diff line number Diff line change
@@ -1,50 +1,55 @@
import { useContext, useState, useEffect, createContext } from 'react';
import { User, UserCredentials } from '@supabase/supabase-js';
import { User } from '@supabase/supabase-js';

import { supabase } from '../libs/supabase';
import { AuthContextModel } from '../models/auth.model';
import { ADMIN_DASHBOARD_BASE_PATH } from '../constants';

const AuthContext = createContext<any>(null);
const AuthContext = createContext<AuthContextModel | null>(null);

export const AuthProvider: React.FC = ({ children }) => {
const [user, setUser] = useState<User | null>()
const [loading, setLoading] = useState(true)
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);

useEffect(() => {
// Check active sessions and sets the user
const session = supabase.auth.session()
const session = supabase.auth.session();

setUser(session?.user ?? null)
setLoading(false)
setUser(session?.user ?? null);
setLoading(false);

// Listen for changes on auth state (logged in, signed out, etc.)
const { data: listener } = supabase.auth.onAuthStateChange(
async (event, session) => {
setUser(session?.user ?? null)
setLoading(false)
async (_event, session) => {
setUser(session?.user ?? null);
setLoading(false);
}
)
);

return () => {
listener?.unsubscribe()
}
}, [])
listener?.unsubscribe();
};
}, []);

// Will be passed down to Signup, Login and Dashboard components
// Will be passed down to components
const value = {
signUp: (data: UserCredentials) => supabase.auth.signUp(data),
signIn: (data: UserCredentials) => supabase.auth.signIn(data),
signIn: () => supabase.auth.signIn({
provider: 'slack',
}, {
redirectTo: `${window.location.origin}${ADMIN_DASHBOARD_BASE_PATH}`
}),
signOut: () => supabase.auth.signOut(),
user,
loading,
}
};

return (
<AuthContext.Provider value={value} >
{!loading && children}
{children}
</AuthContext.Provider>
)
);
};

export const useAuth = () => {
return useContext(AuthContext);
}
};
8 changes: 8 additions & 0 deletions apps/sumilan-app/src/app/models/auth.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { User, GoTrueClient } from "@supabase/supabase-js";

export interface AuthContextModel {
signIn: () => ReturnType<GoTrueClient['signIn']>;
signOut: () => ReturnType<GoTrueClient['signOut']>;
user: User | null;
loading: boolean;
};
13 changes: 13 additions & 0 deletions apps/sumilan-app/src/app/pages/Admin/Admin.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { IonContent, IonPage } from "@ionic/react";

const Admin = () => {
return (
<IonPage>
<IonContent>
admin content
</IonContent>
</IonPage>
);
};

export default Admin;
Loading

0 comments on commit 5cfc757

Please sign in to comment.