Pracino · Issue
Detail nabídky — redesign handoff
Reviewed against your comments — requirements confirmed, the three open questions resolved. This is the build-ready brief: current → target mapping, field reference, and files to touch. Candidate + offer detail get one shared skeleton.
01The requirements (confirmed on review)
My read of issue #180 + the call, restated tightly and confirmed against your comments. All eight agreed; R5 was refined per your note.
- R1 · One shared skeleton. Candidate detail and offer detail share the same vertical structure. The candidate screen is the reference — it's already closest.
- R2 · Shared order (both). Name block → price card → main attributes (icon rows) → secondary attributes (badges) → contacts.
- R3 · The attribute rule. Every attribute becomes an icon row, except skills and languages, which stay badges. Resolves the issue's cut-off last bullet.
- R4 · Candidate — one move only. Layout is already right; the only change is moving the price/salary card from the bottom up under the header.
- R5 · Offer — address beside the logo. Today the address is a full-width row below the logo+name row. Move it up into the text column next to the logo, directly under the company name — so name and address stack together beside the logo.
- R6 · Offer — description, no heading. Description moves directly under the header group, with the "Popis nabídky" heading removed — raw text only.
- R7 · Offer — badges become icons. Profession, work mode, working hours (and úvazek / typ spolupráce) convert from badges to icon rows; skills + languages stay badges.
- R8 · Out of scope. Heading-font unification, description "read more", nav arrows / hamburger — all other issues.
Decided live on the call: skills is free text, and languages were always kept aside. Everything else is a labelled value that reads better as an icon row.
02Resolved on review
The three open questions, now decided from your comments.
- Q1 — úvazek + typ spolupráce → icon rows. Pull both out of the price card into the icon list; the price card becomes salary-only (matching the candidate). Flagged as an optional later tweak — worth exploring a nicer presentation than a plain icon row, but the icon list is the default for now.
- Q2 — languages stay badges. They do not move to the icon list; skills and languages both remain badges. So the candidate's only change stays the single salary-card move.
- Q3 — description sits above the price card. It goes directly under the header group (logo + name + address), above the price card — the confirmed offer order.
03How it maps to the code
Current render order → target, per screen. Naming quirk: candidate detail lives under features/company/ (a company views candidates); offer detail under features/candidate/.
The offer detail today is 100% badge groups — zero icon rows. The icon treatment (ListItem) only exists on the candidate side, so R7 is a real build, not a tweak.
04The offer conversion, concretely
Illustrative — the pattern for turning each offer badge group into a candidate-style icon row. Reuse ListItem; don't invent a component.
123123456567789101112<OfferDetailBadgeGroupitems={[jobOffer.profession.name]}title={t("offer.detail.section.profession")}<ListItemicon="briefcase"title={t("offer.detail.field.profession")}rightText={jobOffer.profession.name}showChevron={false}/><OfferDetailBadgeGroupitems={jobOffer.workModeIds.map((id) => WORK_MODE_LABELS[id] ?? id)}title={t("offer.detail.section.workMode")}<ListItemicon="clock"title={t("offer.detail.field.workMode")}rightText={jobOffer.workModeIds.map((id) => WORK_MODE_LABELS[id] ?? id).join(", ")}showChevron={false}/>No newline at end of fileNo newline at end of file05Field reference — offer
jobOffer on GetPublishedJobOfferDetailResponseDto. Target treatment per attribute, with the label map to resolve enum ids.
| Attribute | Target | Source |
|---|---|---|
| Profession · profese | ✓ | jobOffer.profession.name |
| Work mode · režim práce | ✓ | jobOffer.workModeIds → WORK_MODE_LABELS |
| Working hours · pracovní doba | ✓ | jobOffer.workingHourIds → WORKING_HOUR_LABELS |
| Employment load · úvazek | ✓ | jobOffer.employmentLoadIds → EMPLOYMENT_LOAD_LABELS |
| Work type · typ spolupráce | ✓ | jobOffer.workTypeIds → WORK_TYPE_LABELS |
| Skills · dovednosti | ✕ | jobOffer.skillNames |
| Languages · jazyky | ✕ | jobOffer.languageIds → LANGUAGE_LABELS |
| Salary | card | jobOffer.salary → formatSalary() |
| Description | text | jobOffer.description · drop heading offer.detail.section.description |
| Address | header | jobOffer.location → formatAddressDtoLabel() |
✓ = icon row (ListItem) · ✕ = badge (Badge). Candidate fields mirror these on anonymizedProfile (professions, employmentLoadIds, workTypeIds, workModeIds, workingHourIds, languages, skills, minimumCompensation).
06Components to reuse
All already in the shared UI kit — don't invent equivalents.
- Icon attribute row —
ListItem·shared/components/ui/list-item.tsx·<ListItem icon="briefcase" title=… rightText=… showChevron={false} /> - Badge / chip —
Badge·shared/components/ui/badge.tsx·<Badge text=… variant="primary" />(variants: default·primary·accent·neutral·warning·info) - Card —
Card·shared/components/ui/card.tsx(price card, O firmě card) - Section wrappers — offer:
OfferDetailSection,OfferDetailBadgeGroup· candidate:CandidateDetailChipGroup - Icons already chosen on the candidate — profession
briefcase, modeclock, hourswatch, loadlayers, typebriefcase. Mirror them on the offer.
07Files to touch
Three files carry the change; the O firmě card is left alone.
- src
- features
- candidate
- components
- offer-detail-content.tsxreorder; badge groups → ListItem icon rows; description under header (raw, no heading) above price card; move load/type chips out of price card into icon rows
- offer-detail-header.tsxR5: move address up into the text column beside the logo, under the company name
- components
- company
- components
- candidate-detail-content.tsxmove salary Card up under the header — the only change; languages stay badges
- components
- candidate
- features
Correction from review: the detail screens are full route screens (own back-arrow header, fixed CTA action bar, swipe pager) — opened from the map's bottom-sheet card or the floating pin card, not rendered inside the sheet. This change is layout-only; it adds no native or animation surface, so no iOS-specific gotcha is expected.