Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
1db0a71
feat(igx-templates): add side-nav-mini project template with collapsi…
ivanvpetrov Jun 2, 2026
d3019de
fix(cli:templates): align angular side-nav template layout
georgianastasov Jun 3, 2026
0cc556b
fix(cli:templates): align web components side-nav template layout
georgianastasov Jun 3, 2026
b5d44a4
fix(cli:templates): align react side-nav template layout
georgianastasov Jun 3, 2026
074c4cf
fix(templates): add navigation role to resources container
georgianastasov Jun 3, 2026
fba1438
Potential fix for pull request finding
georgianastasov Jun 3, 2026
0576410
Potential fix for pull request finding
georgianastasov Jun 3, 2026
65604f2
Potential fix for pull request finding
georgianastasov Jun 3, 2026
fb5da8a
Potential fix for pull request finding
georgianastasov Jun 3, 2026
278ac98
feat(templates): add side-nav-mini template (collapsible mini navigat…
ivanvpetrov Jun 3, 2026
4943879
Merge branch 'master' into ipetrov/new-mini-side-nav-template
ivanvpetrov Jun 3, 2026
a03548c
fix(cli:templates): align side-nav resources layout responsiveness
georgianastasov Jun 3, 2026
44e8a8a
Merge branch 'ganastasov/align-side-nav-templates' of https://github.…
georgianastasov Jun 3, 2026
1ce3b97
Merge branch 'ganastasov/align-side-nav-templates' into ipetrov/new-m…
ivanvpetrov Jun 3, 2026
03fa783
feat(templates): side-nav-mini template for React and WC now match th…
ivanvpetrov Jun 3, 2026
84ec5aa
Potential fix for pull request finding
kdinev Jun 3, 2026
cc43e19
Potential fix for pull request finding
kdinev Jun 3, 2026
ca6feb7
feat(templates): fix side-nav-mini animation
ivanvpetrov Jun 3, 2026
59c0b96
fix(cli:templates): update icons
georgianastasov Jun 3, 2026
097829c
feat(templates): fix side-nav-mini icon size
ivanvpetrov Jun 3, 2026
87430f4
Merge branch 'ganastasov/align-side-nav-templates' into ipetrov/new-m…
ivanvpetrov Jun 3, 2026
ad6ec27
feat(templates): fix some code according to base PR
ivanvpetrov Jun 3, 2026
ecc02bb
Potential fix for pull request finding
ivanvpetrov Jun 3, 2026
16a0d35
Potential fix for pull request finding
ivanvpetrov Jun 4, 2026
c3909d0
Potential fix for pull request finding
ivanvpetrov Jun 4, 2026
85ff491
fix(cli:templates): polish angular side nav auth template UI
georgianastasov Jun 4, 2026
097be86
feat(cli:templates): add auth selection step for navigation templates
georgianastasov Jun 4, 2026
fd6aff7
test(cli:templates): restore register selector in auth template
georgianastasov Jun 4, 2026
233e9ae
fix(cli:templates): handle missing project template error and improve…
georgianastasov Jun 4, 2026
07138cf
chore(templates): name option changed for improved readability
ivanvpetrov Jun 4, 2026
315e956
fix(styles): change text to uppercase for submit buttons
georgianastasov Jun 4, 2026
fec5e04
Merge branch 'master' into ipetrov/new-mini-side-nav-template
kdinev Jun 4, 2026
fcb3ddd
fix(templates): css variables redundancy fixed
ivanvpetrov Jun 4, 2026
683c79f
Merge branch 'ipetrov/new-mini-side-nav-template' into ganastasov/sid…
ivanvpetrov Jun 4, 2026
ed22bdb
fix(templates): css variables fixed
ivanvpetrov Jun 4, 2026
099cb69
Merge branch 'master' into ganastasov/side-nav-aut
ivanvpetrov Jun 4, 2026
6a447fc
feat(templates): add auth template option for React and WC
ivanvpetrov Jun 5, 2026
7ea78e2
feat(templates): auth template for React and WC now working
ivanvpetrov Jun 5, 2026
9305bbb
feat(templates): visual improvements
ivanvpetrov Jun 5, 2026
6c1d77e
Merge branch 'master' into ganastasov/side-nav-auth
georgianastasov Jun 5, 2026
0bdf7e7
feat(auth): update styles for react and web components auth templates
georgianastasov Jun 5, 2026
2b2266d
feat(templates): password clear after login
ivanvpetrov Jun 5, 2026
fa1c89e
fix(auth): avoid storing plaintext fake passwords
Copilot Jun 5, 2026
c2d9674
Merge branch 'master' into ganastasov/side-nav-auth
georgianastasov Jun 5, 2026
963a6be
feat(templates): implement external OAuth Google, Facebook, and Micro…
georgianastasov Jun 5, 2026
1380e48
refactor(auth): update plain element with igniteui components
georgianastasov Jun 5, 2026
a94ccef
test(templates): update prompt session specs for auth template
georgianastasov Jun 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import Home from './home/home';
import Profile from './authentication/pages/Profile';
import RedirectGoogle from './authentication/pages/RedirectGoogle';
import RedirectMicrosoft from './authentication/pages/RedirectMicrosoft';
import RedirectFacebook from './authentication/pages/RedirectFacebook';
import { AuthGuard } from './authentication/AuthGuard';

export const routes = [
{ path: '/', element: <Home />, text: 'Home', icon: 'home' },
{
path: '/auth/profile',
element: (
<AuthGuard>
<Profile />
</AuthGuard>
),
text: 'Profile',
icon: 'account_circle',
requiresAuth: true
},
{ path: '/auth/redirect-google', element: <RedirectGoogle /> },
{ path: '/auth/redirect-microsoft', element: <RedirectMicrosoft /> },
{ path: '/auth/redirect-facebook', element: <RedirectFacebook /> },
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
.app {
display: flex;
flex-flow: column nowrap;
height: 100%;
overflow: hidden;
}

.app__navbar {
display: flex;
align-items: center;
flex: 0 0 auto;
height: 56px;
padding: 0 16px;
background: #239ef0;
box-shadow: 0 2px 4px rgba(0, 0, 0, .24);
box-sizing: border-box;
position: relative;
z-index: 10;
}

.app__navbar-spacer {
flex: 1 1 auto;
}

.app__title {
margin: 0 0 0 16px;
font-size: 1.25rem;
font-weight: 600;
line-height: 1;
color: #000;
}

.app__menu-button {
display: inline-flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
padding: 0;
color: #000;
border: 0;
background: transparent;
cursor: pointer;
}

.app__menu-button igc-icon {
font-size: 24px;
}

.app__body {
display: flex;
flex: 1 1 auto;
min-height: 0;
}

.app__drawer {
flex: 0 0 auto;
height: 100%;
--menu-full-width: 280px;
}

igc-nav-drawer-item[active]::part(base) {
background: #e0f2ff;
color: #0075d2;
}

igc-nav-drawer-item[active] igc-icon {
color: #0075d2;
}

.content {
flex: 1 1 auto;
display: flex;
flex-flow: row nowrap;
justify-content: center;
align-items: stretch;
min-width: 0;
overflow: auto;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { useEffect, useMemo, useState } from "react";
import { Outlet, useLocation, useNavigate } from "react-router-dom";
import {
IgrIcon,
IgrNavDrawer,
IgrNavDrawerItem,
registerIcon,
} from "igniteui-react";
import { AuthProvider, useAuth } from "./authentication/AuthContext";
import { LoginBar } from "./authentication/components/LoginBar";
import { routes } from "./app-routes";
import "igniteui-webcomponents/themes/light/bootstrap.css";
import "./app.css";

const materialIcons = [
['home', 'action/svg/production/ic_home_24px.svg'],
['menu', 'navigation/svg/production/ic_menu_24px.svg'],
['apps', 'navigation/svg/production/ic_apps_24px.svg'],
['code', 'action/svg/production/ic_code_24px.svg'],
['build', 'action/svg/production/ic_build_24px.svg'],
['palette', 'image/svg/production/ic_palette_24px.svg'],
['account_circle', 'action/svg/production/ic_account_circle_24px.svg'],
['lock', 'action/svg/production/ic_lock_24px.svg'],
['assignment_ind', 'action/svg/production/ic_assignment_ind_24px.svg'],
] as const;

materialIcons.forEach(([name, path]) =>
registerIcon(name, `https://unpkg.com/material-design-icons@3.0.1/${path}`, "material")
);

function AppContent() {
const name = "$(name)";
const location = useLocation();
const navigate = useNavigate();
const { currentUser } = useAuth();
const [drawerOpen, setDrawerOpen] = useState(true);
const [drawerPosition, setDrawerPosition] = useState<"relative" | "start">("relative");

const visibleRoutes = useMemo(() => {
return routes.filter((route) => {
if (!route.path || !route.text) return false;
if ((route as any).requiresAuth && !currentUser) return false;
return true;
});
}, [currentUser]);

useEffect(() => {
const mediaQuery = window.matchMedia("(min-width: 1025px)");
const updateDrawerState = () => {
setDrawerOpen(mediaQuery.matches);
setDrawerPosition(mediaQuery.matches ? "relative" : "start");
};

updateDrawerState();
mediaQuery.addEventListener("change", updateDrawerState);

return () => mediaQuery.removeEventListener("change", updateDrawerState);
}, []);

const handleRouteClick = (path: string) => {
navigate(path);

if (window.matchMedia("(max-width: 1024px)").matches) {
setDrawerOpen(false);
}
};

return (
<div className="app">
<header className="app__navbar">
<button
className="app__menu-button"
type="button"
aria-label="Toggle navigation"
onClick={() => setDrawerOpen((open) => !open)}
>
<IgrIcon name="menu" collection="material" />
</button>
<h1 className="app__title">{name}</h1>
<div className="app__navbar-spacer" />
<LoginBar />
</header>
<div className="app__body">
<IgrNavDrawer
className="app__drawer"
open={drawerOpen}
position={drawerPosition}
>
{visibleRoutes.map((route) => (
<IgrNavDrawerItem
key={route.path}
active={location.pathname === route.path}
onClick={() => handleRouteClick(route.path)}
>
<IgrIcon
slot="icon"
name={route.icon || "home"}
collection="material"
style={{
color: location.pathname === route.path ? "#0075D2" : undefined,
}}
/>
<span slot="content">{route.text}</span>
</IgrNavDrawerItem>
))}
</IgrNavDrawer>
<main className="content">
<Outlet />
</main>
</div>
</div>
);
}

export default function App() {
return (
<AuthProvider>
<AppContent />
</AuthProvider>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { createContext, useContext, useState, useCallback, type ReactNode } from 'react';
import type { User } from './models/user';
import type { Login } from './models/login';
import type { RegisterInfo } from './models/register-info';
import type { ExternalLogin } from './models/external-login';
import { Authentication } from './services/authentication';
import { UserStore } from './services/userStore';
import { ExternalAuth } from './services/externalAuth';

interface AuthContextType {
currentUser: User | null;
initials: string | null;
login: (data: Login) => Promise<string | null>;
register: (data: RegisterInfo) => Promise<string | null>;
loginWith: (data: ExternalLogin) => Promise<string | null>;
logout: () => void;
}

const AuthContext = createContext<AuthContextType | null>(null);

export function AuthProvider({ children }: { children: ReactNode }) {
const [currentUser, setCurrentUser] = useState<User | null>(() => UserStore.getUser());

const initials = currentUser ? UserStore.getInitials(currentUser) : null;

const login = useCallback(async (data: Login): Promise<string | null> => {
const result = await Authentication.login(data);
if (result.user) {
UserStore.setUser(result.user);
setCurrentUser(result.user);
return null;
}
return result.error ?? 'Login failed';
}, []);

const register = useCallback(async (data: RegisterInfo): Promise<string | null> => {
const result = await Authentication.register(data);
if (result.user) {
UserStore.setUser(result.user);
setCurrentUser(result.user);
return null;
}
return result.error ?? 'Registration failed';
}, []);

const loginWith = useCallback(async (data: ExternalLogin): Promise<string | null> => {
const result = await Authentication.loginWith(data);
if (result.user) {
UserStore.setUser(result.user);
setCurrentUser(result.user);
return null;
}
return result.error ?? 'Social login failed';
}, []);

const logout = useCallback(() => {
ExternalAuth.logout();
UserStore.clearUser();
setCurrentUser(null);
}, []);

return (
<AuthContext.Provider value={{ currentUser, initials, login, register, loginWith, logout }}>
{children}
</AuthContext.Provider>
);
}

export function useAuth() {
const ctx = useContext(AuthContext);
if (!ctx) throw new Error('useAuth must be used within AuthProvider');
return ctx;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Navigate, useLocation } from 'react-router-dom';
import { useAuth } from './AuthContext';
import type { ReactNode } from 'react';

export function AuthGuard({ children }: { children: ReactNode }) {
const { currentUser } = useAuth();
const location = useLocation();

if (!currentUser) {
return <Navigate to="/" state={{ returnUrl: location.pathname }} replace />;
}

return <>{children}</>;
}
Loading