🚀 Hotfixes: inline PDF preview and instant profile update in UI
This commit is contained in:
@@ -18,7 +18,9 @@ import {
|
|||||||
UploadedFile,
|
UploadedFile,
|
||||||
UseInterceptors,
|
UseInterceptors,
|
||||||
ParseUUIDPipe,
|
ParseUUIDPipe,
|
||||||
|
Res,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
|
import { Response } from 'express';
|
||||||
import { FileInterceptor } from '@nestjs/platform-express';
|
import { FileInterceptor } from '@nestjs/platform-express';
|
||||||
import { InvoicesService } from './invoice.service';
|
import { InvoicesService } from './invoice.service';
|
||||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
||||||
@@ -111,7 +113,25 @@ export class InvoicesController {
|
|||||||
async getFile(
|
async getFile(
|
||||||
@CurrentUser() user: any,
|
@CurrentUser() user: any,
|
||||||
@Param('id', ParseUUIDPipe) id: string,
|
@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';
|
} from 'lucide-react';
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
import apiClient from '../../api/client';
|
import apiClient from '../../api/client';
|
||||||
|
import { useAuthStore } from '../../store/authStore';
|
||||||
|
|
||||||
export const SettingsPage = () => {
|
export const SettingsPage = () => {
|
||||||
const [activeTab, setActiveTab] = useState('profile');
|
const [activeTab, setActiveTab] = useState('profile');
|
||||||
@@ -37,6 +38,8 @@ export const SettingsPage = () => {
|
|||||||
language: 'العربية'
|
language: 'العربية'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const updateUser = useAuthStore((state) => state.updateUser);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchProfile = async () => {
|
const fetchProfile = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -62,6 +65,7 @@ export const SettingsPage = () => {
|
|||||||
setIsSaving(true);
|
setIsSaving(true);
|
||||||
try {
|
try {
|
||||||
await apiClient.post('/users/profile', formData);
|
await apiClient.post('/users/profile', formData);
|
||||||
|
updateUser({ name: formData.name });
|
||||||
setShowSuccess(true);
|
setShowSuccess(true);
|
||||||
setTimeout(() => setShowSuccess(false), 3000);
|
setTimeout(() => setShowSuccess(false), 3000);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ interface AuthState {
|
|||||||
isAuthenticated: boolean;
|
isAuthenticated: boolean;
|
||||||
setAuth: (user: User, token: string) => void;
|
setAuth: (user: User, token: string) => void;
|
||||||
clearAuth: () => void;
|
clearAuth: () => void;
|
||||||
|
updateUser: (data: Partial<User>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useAuthStore = create<AuthState>()(
|
export const useAuthStore = create<AuthState>()(
|
||||||
@@ -35,6 +36,11 @@ export const useAuthStore = create<AuthState>()(
|
|||||||
localStorage.removeItem('access_token');
|
localStorage.removeItem('access_token');
|
||||||
set({ user: null, isAuthenticated: false });
|
set({ user: null, isAuthenticated: false });
|
||||||
},
|
},
|
||||||
|
updateUser: (data) => {
|
||||||
|
set((state) => ({
|
||||||
|
user: state.user ? { ...state.user, ...data } : null
|
||||||
|
}));
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: 'musadaq-auth-storage',
|
name: 'musadaq-auth-storage',
|
||||||
|
|||||||
Reference in New Issue
Block a user