From 77434fa815b56234f0e4cce469c96c851d0606e8 Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Sat, 18 Apr 2026 00:47:37 +0300 Subject: [PATCH] Add JoFotara linking modal and fix company limit --- backend/src/modules/auth/auth.service.ts | 20 ++-- fix-limit.js | 12 +++ .../src/pages/companies/CompaniesPage.tsx | 94 ++++++++++++++++++- 3 files changed, 113 insertions(+), 13 deletions(-) create mode 100644 fix-limit.js diff --git a/backend/src/modules/auth/auth.service.ts b/backend/src/modules/auth/auth.service.ts index a714934..3dc7d8f 100644 --- a/backend/src/modules/auth/auth.service.ts +++ b/backend/src/modules/auth/auth.service.ts @@ -27,7 +27,7 @@ export class AuthService { private jwtService: JwtService, private configService: ConfigService, private dataSource: DataSource, - ) {} + ) { } /** * تسجيل مستخدم جديد (مدير مكتب) @@ -60,7 +60,7 @@ export class AuthService { const subscription = queryRunner.manager.create(Subscription, { tenant_id: savedTenant.id, plan: SubscriptionPlan.BASIC, - max_companies: 1, + max_companies: 3, // -1 means unlimited max_invoices_per_month: 200, price_jod: 15, // Basic price status: SubscriptionStatus.ACTIVE, @@ -109,10 +109,10 @@ export class AuthService { throw new UnauthorizedException('Invalid credentials'); } - const payload = { - sub: user.id, - tenantId: user.tenant_id, - role: user.role + const payload = { + sub: user.id, + tenantId: user.tenant_id, + role: user.role }; const accessToken = await this.jwtService.signAsync(payload); @@ -152,10 +152,10 @@ export class AuthService { throw new UnauthorizedException('Access Denied'); } - const payload = { - sub: user.id, - tenantId: user.tenant_id, - role: user.role + const payload = { + sub: user.id, + tenantId: user.tenant_id, + role: user.role }; const accessToken = await this.jwtService.signAsync(payload); diff --git a/fix-limit.js b/fix-limit.js new file mode 100644 index 0000000..163b8c5 --- /dev/null +++ b/fix-limit.js @@ -0,0 +1,12 @@ +const { Pool } = require('pg'); +const pool = new Pool({ + user: 'postgres', + host: '127.0.0.1', + database: 'musadaq', + password: 'postgres_password', + port: 5432, +}); +pool.query("UPDATE subscriptions SET max_companies = -1", (err, res) => { + console.log(err ? err : "Updated successfully!"); + pool.end(); +}); diff --git a/frontend/src/pages/companies/CompaniesPage.tsx b/frontend/src/pages/companies/CompaniesPage.tsx index 346b0c6..81c4d96 100644 --- a/frontend/src/pages/companies/CompaniesPage.tsx +++ b/frontend/src/pages/companies/CompaniesPage.tsx @@ -13,12 +13,18 @@ export const CompaniesPage = () => { const [isLoading, setIsLoading] = useState(true); const [searchTerm, setSearchTerm] = useState(''); const [isAddModalOpen, setIsAddModalOpen] = useState(false); + const [isJoFotaraModalOpen, setIsJoFotaraModalOpen] = useState(false); + const [selectedCompany, setSelectedCompany] = useState(null); - // Form State + // Form State (New Company) const [name, setName] = useState(''); const [tin, setTin] = useState(''); const [address, setAddress] = useState(''); + // Form State (JoFotara) + const [clientId, setClientId] = useState(''); + const [secretKey, setSecretKey] = useState(''); + const fetchCompanies = async () => { try { const { data } = await apiClient.get('/companies'); @@ -53,6 +59,29 @@ export const CompaniesPage = () => { } }; + const handleOpenJoFotara = (company: any) => { + setSelectedCompany(company); + setClientId(''); // We don't fetch existing keys for security, user has to enter new ones if they want to update + setSecretKey(''); + setIsJoFotaraModalOpen(true); + }; + + const handleSubmitJoFotara = async (e: React.FormEvent) => { + e.preventDefault(); + try { + await apiClient.put(`/companies/${selectedCompany.id}/jofotara`, { + clientId, + secretKey + }); + setIsJoFotaraModalOpen(false); + setSelectedCompany(null); + fetchCompanies(); // Refresh to show "Linked" status + } catch (error) { + console.error('Failed to link JoFotara', error); + alert('حدث خطأ أثناء ربط حساب جو فوترة'); + } + }; + const filteredCompanies = companies.filter(c => c.name.includes(searchTerm) || c.tax_identification_number?.includes(searchTerm) ); @@ -131,8 +160,11 @@ export const CompaniesPage = () => { )} - @@ -196,6 +228,62 @@ export const CompaniesPage = () => { )} + + {/* ── JoFotara Link Modal ───────────────────────────────── */} + {isJoFotaraModalOpen && ( +
+
+
+
+ +
+
+

ربط نظام جو فوترة

+

{selectedCompany?.name}

+
+
+
+
+ + setClientId(e.target.value)} + className="w-full bg-slate-50 border border-slate-200 rounded-xl px-4 py-3 outline-none focus:border-primary-500 focus:ring-2 focus:ring-primary-500/20 transition-all font-mono text-sm" + placeholder="أدخل Client ID..." + /> +
+
+ + setSecretKey(e.target.value)} + className="w-full bg-slate-50 border border-slate-200 rounded-xl px-4 py-3 outline-none focus:border-primary-500 focus:ring-2 focus:ring-primary-500/20 transition-all font-mono text-sm" + placeholder="أدخل Secret Key..." + /> +
+
+ + +
+
+
+
+ )} ); };