UI styling improvements: dashboard headers and navigation
- Restore blue PageHeader on Dashboard (/app-components) - Update homepage (/) with subtle header design without blue bar - Add uniform PageHeader styling to application edit page - Fix Rapporten link on homepage to point to /reports overview - Improve header descriptions spacing for better readability
This commit is contained in:
@@ -5,6 +5,7 @@ import {
|
||||
getApplicationById,
|
||||
getConfig,
|
||||
getRelatedObjects,
|
||||
refreshApplication,
|
||||
RelatedObject,
|
||||
} from '../services/api';
|
||||
import { StatusBadge, BusinessImportanceBadge } from './ApplicationList';
|
||||
@@ -133,6 +134,8 @@ export default function ApplicationInfo() {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [jiraHost, setJiraHost] = useState<string>('');
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
const [refreshMessage, setRefreshMessage] = useState<string | null>(null);
|
||||
|
||||
// Use centralized effort calculation hook
|
||||
const { calculatedFte, breakdown: effortBreakdown } = useEffortCalculation({
|
||||
@@ -244,6 +247,44 @@ export default function ApplicationInfo() {
|
||||
setGovernanceExpanded(prev => !prev);
|
||||
};
|
||||
|
||||
const handleRefresh = async () => {
|
||||
if (!id || refreshing) return;
|
||||
|
||||
setRefreshing(true);
|
||||
setRefreshMessage(null);
|
||||
|
||||
try {
|
||||
const result = await refreshApplication(id);
|
||||
setRefreshMessage('Applicatie succesvol gesynchroniseerd vanuit Jira');
|
||||
|
||||
// Reload the application data after a short delay to show the success message
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
const refreshedApp = await getApplicationById(id);
|
||||
setApplication(refreshedApp);
|
||||
// Clear success message after 3 seconds
|
||||
setTimeout(() => {
|
||||
setRefreshMessage(null);
|
||||
}, 3000);
|
||||
} catch (err) {
|
||||
setRefreshMessage('Applicatie gesynchroniseerd, maar herladen mislukt. Ververs de pagina.');
|
||||
// Clear error message after 5 seconds
|
||||
setTimeout(() => {
|
||||
setRefreshMessage(null);
|
||||
}, 5000);
|
||||
}
|
||||
}, 1000);
|
||||
} catch (err) {
|
||||
setRefreshMessage(err instanceof Error ? err.message : 'Synchronisatie mislukt');
|
||||
// Clear error message after 5 seconds
|
||||
setTimeout(() => {
|
||||
setRefreshMessage(null);
|
||||
}, 5000);
|
||||
} finally {
|
||||
setRefreshing(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-slate-50 via-blue-50/30 to-slate-50 flex items-center justify-center">
|
||||
@@ -391,6 +432,24 @@ export default function ApplicationInfo() {
|
||||
</a>
|
||||
</Tooltip>
|
||||
)}
|
||||
<Tooltip text="Synchroniseer applicatie vanuit Jira">
|
||||
<button
|
||||
onClick={handleRefresh}
|
||||
disabled={refreshing}
|
||||
className="inline-flex items-center justify-center w-10 h-10 bg-white hover:bg-gray-50 text-blue-600 rounded-lg transition-all shadow-sm hover:shadow-md disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
{refreshing ? (
|
||||
<svg className="w-5 h-5 animate-spin" fill="none" viewBox="0 0 24 24">
|
||||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
|
||||
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" />
|
||||
</svg>
|
||||
) : (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
||||
</svg>
|
||||
)}
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -402,6 +461,34 @@ export default function ApplicationInfo() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Refresh message */}
|
||||
{refreshMessage && (
|
||||
<div className={`px-6 lg:px-8 py-3 border-b ${
|
||||
refreshMessage.includes('succesvol') || refreshMessage.includes('gesynchroniseerd')
|
||||
? 'bg-green-50 border-green-200'
|
||||
: 'bg-yellow-50 border-yellow-200'
|
||||
}`}>
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
{refreshMessage.includes('succesvol') || refreshMessage.includes('gesynchroniseerd') ? (
|
||||
<svg className="w-5 h-5 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
) : (
|
||||
<svg className="w-5 h-5 text-yellow-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
)}
|
||||
<span className={
|
||||
refreshMessage.includes('succesvol') || refreshMessage.includes('gesynchroniseerd')
|
||||
? 'text-green-800'
|
||||
: 'text-yellow-800'
|
||||
}>
|
||||
{refreshMessage}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Reference warning - only show if reference is truly empty */}
|
||||
{(() => {
|
||||
const refValue = application.reference;
|
||||
|
||||
Reference in New Issue
Block a user