feature: dashboard settings and invite activation flow consistency fixed. frontend warnings fixed

This commit is contained in:
2026-04-30 14:05:44 +03:30
parent 1b8856f64e
commit 1387e4cb5e
38 changed files with 375 additions and 362 deletions

View File

@@ -48,8 +48,9 @@ export class AuthService {
*/
async validateUser(email: string, password: string): Promise<any> {
try {
const normalizedEmail = email.trim().toLowerCase();
const user = await this.prisma.user.findUnique({
where: { email },
where: { email: normalizedEmail },
include: {
memberships: {
include: {
@@ -147,6 +148,7 @@ export class AuthService {
? {
name: membership.organization.plan.name,
maxUsers: membership.organization.plan.maxUsers,
price: membership.organization.plan.price,
}
: undefined,
}));
@@ -177,7 +179,8 @@ export class AuthService {
* @returns Created user info without password
*/
async register(registerDto: RegisterDto) {
const { email, password, name, organizationName, organizationEmail, organizationType } = registerDto;
const { password, name, organizationName, organizationEmail, organizationType } = registerDto;
const email = registerDto.email.trim().toLowerCase();
// 1. Check existing user
const existingUser = await this.prisma.user.findUnique({
@@ -354,6 +357,7 @@ export class AuthService {
? {
name: membership.organization.plan.name,
maxUsers: membership.organization.plan.maxUsers,
price: membership.organization.plan.price,
}
: undefined,
}));
@@ -474,6 +478,7 @@ export class AuthService {
? {
name: membership.organization.plan.name,
maxUsers: membership.organization.plan.maxUsers,
price: membership.organization.plan.price,
}
: undefined,
}));
@@ -680,6 +685,7 @@ export class AuthService {
? {
name: membership.organization.plan.name,
maxUsers: membership.organization.plan.maxUsers,
price: membership.organization.plan.price,
}
: undefined,
}));
@@ -759,6 +765,7 @@ export class AuthService {
? {
name: membership.organization.plan.name,
maxUsers: membership.organization.plan.maxUsers,
price: membership.organization.plan.price,
}
: undefined,
},
@@ -770,7 +777,12 @@ export class AuthService {
memberships: Array<{
isOwner: boolean;
isActive: boolean;
organization: { id: string; name: string; type: { name: string }; plan?: { name: string; maxUsers: number } | null };
organization: {
id: string;
name: string;
type: { name: string };
plan?: { name: string; maxUsers: number; price: number } | null;
};
permissions?: Array<{ permission: { name: string } }>;
}> = [],
) {
@@ -790,6 +802,8 @@ export class AuthService {
seatsLow: false,
trialEndingSoon: false,
trialExpired: false,
daysUntilPlanEnd: null,
planEndsAt: null,
},
};
}
@@ -811,6 +825,8 @@ export class AuthService {
seatsLow: false,
trialEndingSoon: false,
trialExpired: false,
daysUntilPlanEnd: null,
planEndsAt: null,
},
};
}
@@ -830,23 +846,16 @@ export class AuthService {
const seatsLow =
!unlimited && remaining >= 0 && remaining <= 2 && maxUsers > 0;
let trialEndingSoon = false;
let trialExpired = false;
let daysUntilTrialEnd: number | null = null;
let trialEndsAt: string | null = null;
// Current pricing model: trial lasts 30 days; paid plans last 90 days.
const durationDays = plan.name === 'trial' ? 30 : 90;
const end = new Date(org.createdAt);
end.setDate(end.getDate() + durationDays);
const planEndsAt = end.toISOString();
const ms = end.getTime() - Date.now();
const daysUntilPlanEnd = Math.ceil(ms / (1000 * 60 * 60 * 24));
if (plan.name === 'trial') {
const end = new Date(org.createdAt);
end.setDate(end.getDate() + 30);
trialEndsAt = end.toISOString();
const ms = end.getTime() - Date.now();
daysUntilTrialEnd = Math.ceil(ms / (1000 * 60 * 60 * 24));
if (daysUntilTrialEnd <= 0) {
trialExpired = true;
} else if (daysUntilTrialEnd <= 7) {
trialEndingSoon = true;
}
}
const trialExpired = plan.name === 'trial' && daysUntilPlanEnd <= 0;
const trialEndingSoon = plan.name === 'trial' && daysUntilPlanEnd > 0 && daysUntilPlanEnd <= 7;
const showWarning = seatsLow || trialEndingSoon || trialExpired;
@@ -859,8 +868,10 @@ export class AuthService {
trialExpired,
seatsUsed,
seatsLimit: maxUsers,
daysUntilTrialEnd,
trialEndsAt,
daysUntilTrialEnd: plan.name === 'trial' ? daysUntilPlanEnd : null,
trialEndsAt: plan.name === 'trial' ? planEndsAt : null,
daysUntilPlanEnd,
planEndsAt,
},
};
}