🚀 Hotfixes: inline PDF preview and instant profile update in UI
This commit is contained in:
@@ -18,7 +18,9 @@ import {
|
||||
UploadedFile,
|
||||
UseInterceptors,
|
||||
ParseUUIDPipe,
|
||||
Res,
|
||||
} from '@nestjs/common';
|
||||
import { Response } from 'express';
|
||||
import { FileInterceptor } from '@nestjs/platform-express';
|
||||
import { InvoicesService } from './invoice.service';
|
||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
||||
@@ -111,7 +113,25 @@ export class InvoicesController {
|
||||
async getFile(
|
||||
@CurrentUser() user: any,
|
||||
@Param('id', ParseUUIDPipe) id: string,
|
||||
@Res({ passthrough: true }) res: Response,
|
||||
) {
|
||||
return this.invoicesService.getFile(user.tenantId, id);
|
||||
const streamableFile = await this.invoicesService.getFile(user.tenantId, id);
|
||||
|
||||
// We need to determine the content type to ensure it opens inline in the browser (iframe)
|
||||
// We can fetch the invoice details first to get the extension.
|
||||
const invoice = await this.invoicesService.findOne(user.tenantId, id);
|
||||
let mimeType = 'application/pdf'; // Default fallback
|
||||
if (invoice.original_file_path) {
|
||||
const ext = invoice.original_file_path.split('.').pop()?.toLowerCase();
|
||||
if (ext === 'jpg' || ext === 'jpeg') mimeType = 'image/jpeg';
|
||||
else if (ext === 'png') mimeType = 'image/png';
|
||||
}
|
||||
|
||||
res.set({
|
||||
'Content-Type': mimeType,
|
||||
'Content-Disposition': 'inline', // This forces the browser to display it instead of downloading
|
||||
});
|
||||
|
||||
return streamableFile;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
} from 'lucide-react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import apiClient from '../../api/client';
|
||||
import { useAuthStore } from '../../store/authStore';
|
||||
|
||||
export const SettingsPage = () => {
|
||||
const [activeTab, setActiveTab] = useState('profile');
|
||||
@@ -37,6 +38,8 @@ export const SettingsPage = () => {
|
||||
language: 'العربية'
|
||||
});
|
||||
|
||||
const updateUser = useAuthStore((state) => state.updateUser);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchProfile = async () => {
|
||||
try {
|
||||
@@ -62,6 +65,7 @@ export const SettingsPage = () => {
|
||||
setIsSaving(true);
|
||||
try {
|
||||
await apiClient.post('/users/profile', formData);
|
||||
updateUser({ name: formData.name });
|
||||
setShowSuccess(true);
|
||||
setTimeout(() => setShowSuccess(false), 3000);
|
||||
} catch (err) {
|
||||
|
||||
@@ -20,6 +20,7 @@ interface AuthState {
|
||||
isAuthenticated: boolean;
|
||||
setAuth: (user: User, token: string) => void;
|
||||
clearAuth: () => void;
|
||||
updateUser: (data: Partial<User>) => void;
|
||||
}
|
||||
|
||||
export const useAuthStore = create<AuthState>()(
|
||||
@@ -35,6 +36,11 @@ export const useAuthStore = create<AuthState>()(
|
||||
localStorage.removeItem('access_token');
|
||||
set({ user: null, isAuthenticated: false });
|
||||
},
|
||||
updateUser: (data) => {
|
||||
set((state) => ({
|
||||
user: state.user ? { ...state.user, ...data } : null
|
||||
}));
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: 'musadaq-auth-storage',
|
||||
|
||||
Reference in New Issue
Block a user