Skip to content
Merged
16 changes: 10 additions & 6 deletions worklenz-backend/src/controllers/auth-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,11 @@ export default class AuthController extends WorklenzControllerBase {
public static async reset_password(req: IWorkLenzRequest, res: IWorkLenzResponse) {
const {email} = req.body;

const q = `SELECT id, email, google_id, password FROM users WHERE email = $1;`;
const result = await db.query(q, [email || null]);
// Normalize email to lowercase for case-insensitive comparison
const normalizedEmail = email ? email.toLowerCase().trim() : null;

const q = `SELECT id, email, google_id, password FROM users WHERE LOWER(email) = $1;`;
const result = await db.query(q, [normalizedEmail]);

if (!result.rowCount)
return res.status(200).send(new ServerResponse(false, null, "Account does not exists!"));
Expand Down Expand Up @@ -297,15 +300,16 @@ export default class AuthController extends WorklenzControllerBase {
}

// Check for existing local account
const localAccountResult = await db.query("SELECT 1 FROM users WHERE email = $1 AND password IS NOT NULL AND is_deleted IS FALSE;", [profile.email]);
const normalizedProfileEmail = profile.email.toLowerCase().trim();
const localAccountResult = await db.query("SELECT 1 FROM users WHERE LOWER(email) = $1 AND password IS NOT NULL AND is_deleted IS FALSE;", [normalizedProfileEmail]);
if (localAccountResult.rowCount) {
return res.status(400).send(new ServerResponse(false, null, `No Google account exists for email ${profile.email}.`));
}

// Check if user exists
const userResult = await db.query(
"SELECT id, google_id, name, email, active_team FROM users WHERE google_id = $1 OR email = $2;",
[profile.sub, profile.email]
"SELECT id, google_id, name, email, active_team FROM users WHERE google_id = $1 OR LOWER(email) = $2;",
[profile.sub, normalizedProfileEmail]
);

let user: any;
Expand All @@ -317,7 +321,7 @@ export default class AuthController extends WorklenzControllerBase {
const googleUserData = {
id: profile.sub,
displayName: profile.name,
email: profile.email,
email: normalizedProfileEmail,
picture: profile.picture
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@ async function handleLogin(req: Request, email: string, password: string, done:
}

try {
// Normalize email to lowercase for case-insensitive comparison
const normalizedEmail = email.toLowerCase().trim();

const q = `SELECT id, email, google_id, password
FROM users
WHERE email = $1
WHERE LOWER(email) = $1
AND google_id IS NULL
AND is_deleted IS FALSE;`;
const result = await db.query(q, [email]);
const result = await db.query(q, [normalizedEmail]);

const [data] = result.rows;

Expand All @@ -33,7 +36,7 @@ async function handleLogin(req: Request, email: string, password: string, done:

const passwordMatch = bcrypt.compareSync(password, data.password);

if (passwordMatch && email === data.email) {
if (passwordMatch) {
delete data.password;
const successMsg = "User successfully logged in";
req.flash(SUCCESS_KEY, successMsg);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,21 @@ async function isGoogleAccountFound(email: string) {
const q = `
SELECT 1
FROM users
WHERE email = $1
WHERE LOWER(email) = $1
AND google_id IS NOT NULL;
`;
const result = await db.query(q, [email]);
const result = await db.query(q, [email.toLowerCase().trim()]);
return !!result.rowCount;
}

async function isAccountDeactivated(email: string) {
const q = `
SELECT 1
FROM users
WHERE email = $1
WHERE LOWER(email) = $1
AND is_deleted = TRUE;
`;
const result = await db.query(q, [email]);
const result = await db.query(q, [email.toLowerCase().trim()]);
return !!result.rowCount;
}

Expand All @@ -41,7 +41,7 @@ async function registerUser(password: string, team_id: string, name: string, tea
const body = {
name,
team_name,
email,
email: email.toLowerCase().trim(),
password: encryptedPassword,
timezone,
invited_team_id: teamId,
Expand Down
11 changes: 0 additions & 11 deletions worklenz-backend/src/views/_tawk-to.pug

This file was deleted.

6 changes: 6 additions & 0 deletions worklenz-frontend/public/locales/alb/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@
"trial-badge-hours": "{{hours}} orë të mbetura",
"trial-alert-admin-note": "Ju mund të aksesoni ende Qendrën e Administrimit për të menaxhuar abonimin tuaj",
"trial-alert-dismiss": "Hidhe për sot",
"switch-team-to-continue": "Ndërro skuadrën për të vazhduar",
"current-team": "Skuadra aktuale",
"select-team": "Zgjidh skuadrën",
"owned-by": "Në pronësi të",
"switch-team-active-subscription": "Kaloni në një skuadër me një abonim aktiv për të vazhduar punën",
"or": "ose",
"license-expiring-soon": "Licenca juaj skadon në {{days}} ditë",
"license-expiring-soon_plural": "Licenca juaj skadon në {{days}} ditë",
"license-expiring-today": "Licenca juaj skadon sot!",
Expand Down
6 changes: 6 additions & 0 deletions worklenz-frontend/public/locales/de/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@
"trial-badge-hours": "{{hours}}h übrig",
"trial-alert-admin-note": "Sie können weiterhin auf das Admin Center zugreifen, um Ihr Abonnement zu verwalten",
"trial-alert-dismiss": "Für heute ausblenden",
"switch-team-to-continue": "Team wechseln zum Fortfahren",
"current-team": "Aktuelles Team",
"select-team": "Team auswählen",
"owned-by": "Gehört",
"switch-team-active-subscription": "Wechseln Sie zu einem Team mit einem aktiven Abonnement, um weiterzuarbeiten",
"or": "oder",
"license-expiring-soon": "Ihre Lizenz läuft in {{days}} Tag ab",
"license-expiring-soon_plural": "Ihre Lizenz läuft in {{days}} Tagen ab",
"license-expiring-today": "Ihre Lizenz läuft heute ab!",
Expand Down
6 changes: 6 additions & 0 deletions worklenz-frontend/public/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@
"trial-badge-hours": "{{hours}}h left",
"trial-alert-admin-note": "You can still access the Admin Center to manage your subscription",
"trial-alert-dismiss": "Dismiss for today",
"switch-team-to-continue": "Switch Team to Continue",
"current-team": "Current Team",
"select-team": "Select Team",
"owned-by": "Owned by",
"switch-team-active-subscription": "Switch to a team with an active subscription to continue working",
"or": "or",
"license-expiring-soon": "Your license expires in {{days}} day",
"license-expiring-soon_plural": "Your license expires in {{days}} days",
"license-expiring-today": "Your license expires today!",
Expand Down
6 changes: 6 additions & 0 deletions worklenz-frontend/public/locales/es/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@
"trial-badge-hours": "{{hours}}h restantes",
"trial-alert-admin-note": "Aún puede acceder al Centro de administración para gestionar su suscripción",
"trial-alert-dismiss": "Descartar por hoy",
"switch-team-to-continue": "Cambiar equipo para continuar",
"current-team": "Equipo actual",
"select-team": "Seleccionar equipo",
"owned-by": "Propiedad de",
"switch-team-active-subscription": "Cambie a un equipo con una suscripción activa para continuar trabajando",
"or": "o",
"license-expiring-soon": "Su licencia expira en {{days}} día",
"license-expiring-soon_plural": "Su licencia expira en {{days}} días",
"license-expiring-today": "¡Su licencia expira hoy!",
Expand Down
6 changes: 6 additions & 0 deletions worklenz-frontend/public/locales/pt/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@
"trial-badge-hours": "{{hours}}h restantes",
"trial-alert-admin-note": "Você ainda pode acessar o Centro de administração para gerenciar sua assinatura",
"trial-alert-dismiss": "Dispensar por hoje",
"switch-team-to-continue": "Trocar equipe para continuar",
"current-team": "Equipe atual",
"select-team": "Selecionar equipe",
"owned-by": "Propriedade de",
"switch-team-active-subscription": "Mude para uma equipe com uma assinatura ativa para continuar trabalhando",
"or": "ou",
"license-expiring-soon": "Sua licença expira em {{days}} dia",
"license-expiring-soon_plural": "Sua licença expira em {{days}} dias",
"license-expiring-today": "Sua licença expira hoje!",
Expand Down
6 changes: 6 additions & 0 deletions worklenz-frontend/public/locales/zh/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@
"trial-badge-hours": "剩余{{hours}}小时",
"trial-alert-admin-note": "您仍可以访问管理中心管理您的订阅",
"trial-alert-dismiss": "今日暂不提醒",
"switch-team-to-continue": "切换团队以继续",
"current-team": "当前团队",
"select-team": "选择团队",
"owned-by": "拥有者",
"switch-team-active-subscription": "切换到有有效订阅的团队以继续工作",
"or": "或",
"license-expiring-soon": "您的许可证还有 {{days}} 天到期",
"license-expiring-soon_plural": "您的许可证还有 {{days}} 天到期",
"license-expiring-today": "您的许可证今天到期!",
Expand Down
18 changes: 6 additions & 12 deletions worklenz-frontend/src/app/routes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,21 +74,15 @@ export const LicenseExpiryGuard = memo(({ children }: GuardProps) => {
const currentSession = authService?.getCurrentSession();
const subscriptionType = currentSession?.subscription_type as ISUBSCRIPTION_TYPE;

// If license is expired and not on admin center, block the content entirely
// If license is expired and not on admin center, show modal overlay
if (showModal) {
return (
<div style={{ position: 'relative', height: '100vh', overflow: 'hidden' }}>
<div style={{
position: 'absolute',
inset: 0,
pointerEvents: 'none',
opacity: 0.3,
filter: 'blur(2px)'
}}>
{children}
</div>
<>
{/* Render children normally */}
{children}
{/* Show modal as an overlay */}
<LicenseExpiredModal open={true} subscriptionType={subscriptionType} />
</div>
</>
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
.license-expired-modal-wrap .ant-modal-wrap {
z-index: 1050 !important;
}

.license-expired-modal-wrap .ant-modal-mask {
z-index: 1049 !important;
}

/* Ensure dropdowns in the modal appear above the modal */
.switch-team-dropdown {
z-index: 1060 !important;
}

/* Theme-aware dropdown styling */
.switch-team-dropdown .ant-dropdown-menu {
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}

[data-theme="dark"] .switch-team-dropdown .ant-dropdown-menu {
background-color: #262626;
border: 1px solid #434343;
}

[data-theme="dark"] .switch-team-dropdown .ant-dropdown-menu-item {
color: #fff;
}

[data-theme="dark"] .switch-team-dropdown .ant-dropdown-menu-item:hover {
background-color: #303030;
}

/* Ensure the modal content is properly styled */
.license-expired-modal-wrap .ant-modal-content {
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
}
Loading