Skip to content

Commit

Permalink
Merge pull request #59 from ElemarJR/calendar_on_cases
Browse files Browse the repository at this point in the history
Calendar on cases
  • Loading branch information
ElemarJR authored Dec 5, 2024
2 parents bc22c78 + ceab187 commit 4e056ce
Show file tree
Hide file tree
Showing 6 changed files with 486 additions and 106 deletions.
7 changes: 5 additions & 2 deletions backend/src/api/domain/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from .consultants_engineers import resolve_consultants_and_engineers, resolve_consultant_or_engineer, resolve_consultant_or_engineer_timesheet
from .clients import resolve_client_timesheet, resolve_clients, resolve_client
from .sponsors import resolve_sponsor_timesheet, resolve_sponsors, resolve_sponsor
from .cases import resolve_cases, resolve_case
from .cases import resolve_case_timesheet, resolve_cases, resolve_case
from .offers import resolve_offers, resolve_offer
from .projects import resolve_projects
from .active_deals import resolve_active_deals, resolve_active_deal, compute_active_deals
Expand Down Expand Up @@ -42,4 +42,7 @@ def setup_query_for_domain(query: QueryType):
sponsor_type = ObjectType('Sponsor')
sponsor_type.set_field('timesheet', resolve_sponsor_timesheet)

return [client_type, account_manager_type, consultant_or_engineer_type, sponsor_type]
case_type = ObjectType('Case')
case_type.set_field('timesheet', resolve_case_timesheet)

return [client_type, account_manager_type, consultant_or_engineer_type, sponsor_type, case_type]
19 changes: 18 additions & 1 deletion backend/src/api/domain/cases.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from api.utils.fields import build_fields_map
from backend.src.api.datasets.timesheets import compute_timesheet
import globals


Expand Down Expand Up @@ -86,4 +87,20 @@ def build_case_dictionary(map, case):
'last_six_weeks': computed_timesheet
}

return result
return result

def resolve_case_timesheet(case, info, slug, filters=None):
if filters is None:
filters = []

title = case["title"]

client_filters = [
{
'field': 'CaseTitle',
'selected_values': [title]
}
] + filters

map_ = build_fields_map(info)
return compute_timesheet(map_, slug, filters=client_filters)
2 changes: 2 additions & 0 deletions backend/src/api/domain/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ type Case {
timesheets: Timesheets
isStale: Boolean!
ontologyUrl: String

timesheet(slug: String!, filters: [FilterInput]): TimesheetSummary
}

type TrackerProjectBudget {
Expand Down
122 changes: 73 additions & 49 deletions frontend/src/app/about-us/cases/[slug]/CaseHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,59 +25,83 @@ interface CaseHeaderProps {

export function CaseHeader({ caseItem }: CaseHeaderProps) {
return (
<div className="flex items-center border-b border-zinc-950/10 pb-4 mb-4 dark:border-white/10">
{caseItem.client?.logoUrl && (
<div className="flex items-center h-full mr-6">
<img
src={caseItem.client.logoUrl}
alt={`${caseItem.client.name} logo`}
className="w-32 h-16 object-contain flex-shrink-0"
/>
</div>
)}
<div className="flex flex-col flex-grow space-y-2">
<div>
<div className="flex flex-col">
<Heading className="mt-1">{caseItem.title}</Heading>
{caseItem.ontologyUrl && (
<span className="text-xs mt-1">
<a
href={caseItem.ontologyUrl}
target="_blank"
rel="noopener noreferrer"
className="text-blue-600 hover:text-blue-800"
>
[View Ontology]
</a>
</span>
)}
<div className="bg-white">
<div className="flex items-center">
{caseItem.client?.logoUrl && (
<div className="flex items-center justify-center h-full mr-8 border-r pr-8 border-gray-200">
<img
src={caseItem.client.logoUrl}
alt={`${caseItem.client.name} logo`}
className="w-40 h-20 object-contain flex-shrink-0"
/>
</div>
</div>
<p className="text-sm font-medium text-gray-700">{caseItem.sponsor}</p>
<div className="flex flex-wrap items-center gap-x-3 gap-y-2 text-xs text-gray-600">
<div className="flex items-center space-x-1">
{caseItem.startOfContract && (
<span className="text-green-600">
{new Date(caseItem.startOfContract).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' })}
</span>
)}
{caseItem.startOfContract && caseItem.endOfContract && <span>-</span>}
{caseItem.endOfContract && (
<span className="text-red-600">
{new Date(caseItem.endOfContract).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' })}
</span>
)}
)}
<div className="flex flex-col flex-grow space-y-3">
<div>
<div className="flex flex-col lg:max-w-[80%]">
<Heading className="text-2xl font-bold text-gray-900">{caseItem.title}</Heading>
{caseItem.ontologyUrl && (
<span className="text-xs mt-2">
<a
href={caseItem.ontologyUrl}
target="_blank"
rel="noopener noreferrer"
className="text-blue-600 hover:text-blue-800 hover:underline flex items-center"
>
<svg className="w-3 h-3 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1" />
</svg>
View Ontology
</a>
</span>
)}
</div>
</div>
<div className="flex flex-col gap-2 text-sm">
<p className="font-medium text-gray-700 flex items-center">
<svg className="w-3 h-3 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
</svg>
{caseItem.sponsor}
</p>
<div className="flex items-center gap-2">
<p className="text-gray-700 flex items-center">
<svg className="w-3 h-3 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
</svg>
<span>
{caseItem.startOfContract && (
<span className="text-green-600 font-medium">
{new Date(caseItem.startOfContract).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' })}
</span>
)}
{caseItem.startOfContract && caseItem.endOfContract && <span className="mx-2"></span>}
{caseItem.endOfContract && (
<span className="text-red-600 font-medium">
{new Date(caseItem.endOfContract).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' })}
</span>
)}
</span>
</p>
{caseItem.weeklyApprovedHours && (
<div className="flex items-center text-gray-600">
<span className="mx-2"></span>
<svg className="w-3 h-3 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span>{caseItem.weeklyApprovedHours}h/week {caseItem.preContractedValue && <span className="text-xs text-gray-500">(pre-contracted)</span>}</span>
</div>
)}
</div>
</div>
{caseItem.weeklyApprovedHours && (
<span className="border-l pl-3 border-gray-300">
Hours per week: {caseItem.weeklyApprovedHours}{caseItem.preContractedValue ? ' (pre)' : ''}
<div className="flex flex-wrap items-center gap-x-4 gap-y-2 text-xs text-gray-600">
<span className={`px-3 py-1.5 rounded-md font-medium flex items-center ${caseItem.isActive ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'}`}>
<span className={`w-1.5 h-1.5 rounded-full mr-2 ${caseItem.isActive ? 'bg-green-500' : 'bg-red-500'}`}></span>
{caseItem.isActive ? "Active" : "Inactive"}
</span>
)}
<span className={`px-2 py-1 rounded-full text-xs font-medium ${caseItem.isActive ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'}`}>
{caseItem.isActive ? "Active" : "Inactive"}
</span>
</div>
</div>
</div>
</div>
);
}
}
Loading

0 comments on commit 4e056ce

Please sign in to comment.