From e7a18cd33947da294df2dd5145e33f48980caf0b Mon Sep 17 00:00:00 2001 From: Sven Carstensen Date: Wed, 18 Mar 2026 21:13:29 +0100 Subject: [PATCH] feat: extend farmer data model with complete address fields and customer number Backend: - Add customerNumber, companyName, contactPerson, street, houseNumber, postalCode, city, phoneNumber to Farmer domain model - Update FarmerRow and FarmerMutation records with new fields - Update repositories, services and controllers for new farmer structure - Fix CORS configuration to allow credentials - Remove unused authorizationService and imports - Update data migration for new farmer schema Frontend: - Update FarmerRow and FarmerOption interfaces - Extend AdministrationPage with new farmer form fields - Update SampleRegistrationPage and SearchFarmerPage for new structure --- .../svencarstensen/muh/config/CorsConfig.java | 3 +- .../de/svencarstensen/muh/domain/Farmer.java | 9 +- .../muh/repository/FarmerRepository.java | 8 +- .../muh/service/CatalogService.java | 134 +++++-- .../muh/service/InvoiceService.java | 2 - .../muh/service/PortalService.java | 2 +- .../muh/service/SampleService.java | 4 +- frontend/src/lib/types.ts | 12 +- frontend/src/pages/AdministrationPage.tsx | 343 ++++++++++++++---- frontend/src/pages/SampleRegistrationPage.tsx | 2 +- frontend/src/pages/SearchFarmerPage.tsx | 4 +- frontend/src/styles/global.css | 69 ++++ 12 files changed, 464 insertions(+), 128 deletions(-) diff --git a/backend/src/main/java/de/svencarstensen/muh/config/CorsConfig.java b/backend/src/main/java/de/svencarstensen/muh/config/CorsConfig.java index 14b1ecd..be81e70 100644 --- a/backend/src/main/java/de/svencarstensen/muh/config/CorsConfig.java +++ b/backend/src/main/java/de/svencarstensen/muh/config/CorsConfig.java @@ -18,7 +18,8 @@ public class CorsConfig { configuration.setAllowedOrigins(allowedOrigins); configuration.setAllowedHeaders(List.of("*")); configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS")); - configuration.setAllowCredentials(false); + configuration.setAllowCredentials(true); + configuration.setExposedHeaders(List.of("Authorization")); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/api/**", configuration); diff --git a/backend/src/main/java/de/svencarstensen/muh/domain/Farmer.java b/backend/src/main/java/de/svencarstensen/muh/domain/Farmer.java index 5cb5548..0e575a1 100644 --- a/backend/src/main/java/de/svencarstensen/muh/domain/Farmer.java +++ b/backend/src/main/java/de/svencarstensen/muh/domain/Farmer.java @@ -10,8 +10,15 @@ public record Farmer( @Id String id, String accountId, String businessKey, - String name, + String customerNumber, + String companyName, + String contactPerson, + String street, + String houseNumber, + String postalCode, + String city, String email, + String phoneNumber, boolean active, String supersedesId, LocalDateTime createdAt, diff --git a/backend/src/main/java/de/svencarstensen/muh/repository/FarmerRepository.java b/backend/src/main/java/de/svencarstensen/muh/repository/FarmerRepository.java index ab2ac2a..9b6af0f 100644 --- a/backend/src/main/java/de/svencarstensen/muh/repository/FarmerRepository.java +++ b/backend/src/main/java/de/svencarstensen/muh/repository/FarmerRepository.java @@ -6,11 +6,11 @@ import org.springframework.data.mongodb.repository.MongoRepository; import java.util.List; public interface FarmerRepository extends MongoRepository { - List findByActiveTrueOrderByNameAsc(); + List findByActiveTrueOrderByCompanyNameAsc(); - List findByAccountIdOrderByNameAsc(String accountId); + List findByAccountIdOrderByCompanyNameAsc(String accountId); - List findByAccountIdAndActiveTrueOrderByNameAsc(String accountId); + List findByAccountIdAndActiveTrueOrderByCompanyNameAsc(String accountId); - List findByNameContainingIgnoreCaseOrderByNameAsc(String name); + List findByCompanyNameContainingIgnoreCaseOrderByCompanyNameAsc(String companyName); } diff --git a/backend/src/main/java/de/svencarstensen/muh/service/CatalogService.java b/backend/src/main/java/de/svencarstensen/muh/service/CatalogService.java index d8720d3..d23bd02 100644 --- a/backend/src/main/java/de/svencarstensen/muh/service/CatalogService.java +++ b/backend/src/main/java/de/svencarstensen/muh/service/CatalogService.java @@ -14,7 +14,6 @@ import de.svencarstensen.muh.repository.FarmerRepository; import de.svencarstensen.muh.repository.MedicationCatalogRepository; import de.svencarstensen.muh.repository.PathogenCatalogRepository; import de.svencarstensen.muh.security.AuthTokenService; -import de.svencarstensen.muh.security.AuthorizationService; import org.springframework.http.HttpStatus; import org.springframework.lang.NonNull; import org.springframework.data.mongodb.core.MongoTemplate; @@ -42,7 +41,7 @@ public class CatalogService { private static final Comparator FARMER_ROW_COMPARATOR = Comparator .comparing(FarmerRow::active).reversed() - .thenComparing(FarmerRow::name, String.CASE_INSENSITIVE_ORDER) + .thenComparing(FarmerRow::companyName, String.CASE_INSENSITIVE_ORDER) .thenComparing(FarmerRow::updatedAt, Comparator.nullsLast(Comparator.reverseOrder())); private static final Comparator MEDICATION_ROW_COMPARATOR = Comparator @@ -67,7 +66,6 @@ public class CatalogService { private final AppUserRepository appUserRepository; private final MongoTemplate mongoTemplate; private final AuthTokenService authTokenService; - private final AuthorizationService authorizationService; private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); public CatalogService( @@ -77,8 +75,7 @@ public class CatalogService { AntibioticCatalogRepository antibioticRepository, AppUserRepository appUserRepository, MongoTemplate mongoTemplate, - AuthTokenService authTokenService, - AuthorizationService authorizationService + AuthTokenService authTokenService ) { this.farmerRepository = farmerRepository; this.medicationRepository = medicationRepository; @@ -87,7 +84,6 @@ public class CatalogService { this.appUserRepository = appUserRepository; this.mongoTemplate = mongoTemplate; this.authTokenService = authTokenService; - this.authorizationService = authorizationService; } public ActiveCatalogSummary activeCatalogSummary(String actorId) { @@ -113,7 +109,7 @@ public class CatalogService { // Hilfsmethoden für Datenzugriff (immer nur eigene Daten des Hauptbenutzers) private List listActiveFarmersForActor(AppUser actor) { - return farmerRepository.findByAccountIdAndActiveTrueOrderByNameAsc(resolveAccountId(actor)); + return farmerRepository.findByAccountIdAndActiveTrueOrderByCompanyNameAsc(resolveAccountId(actor)); } private List listActiveMedicationsForActor(AppUser actor) { @@ -129,7 +125,7 @@ public class CatalogService { } private List listFarmerRowsForActor(AppUser actor) { - return farmerRepository.findByAccountIdOrderByNameAsc(resolveAccountId(actor)).stream() + return farmerRepository.findByAccountIdOrderByCompanyNameAsc(resolveAccountId(actor)).stream() .map(this::toFarmerRow) .sorted(FARMER_ROW_COMPARATOR) .toList(); @@ -160,7 +156,7 @@ public class CatalogService { AppUser actor = requireActiveActor(actorId, "Nicht berechtigt"); String accountId = resolveAccountId(actor); for (FarmerMutation mutation : mutations) { - if (isBlank(mutation.name())) { + if (isBlank(mutation.companyName())) { continue; } LocalDateTime now = LocalDateTime.now(); @@ -169,8 +165,15 @@ public class CatalogService { null, accountId, UUID.randomUUID().toString(), - mutation.name().trim(), + blankToNull(mutation.customerNumber()), + mutation.companyName().trim(), + blankToNull(mutation.contactPerson()), + blankToNull(mutation.street()), + blankToNull(mutation.houseNumber()), + blankToNull(mutation.postalCode()), + blankToNull(mutation.city()), blankToNull(mutation.email()), + blankToNull(mutation.phoneNumber()), mutation.active(), null, now, @@ -185,15 +188,29 @@ public class CatalogService { if (!accountId.equals(existing.accountId())) { throw new ResponseStatusException(HttpStatus.FORBIDDEN, "Nicht berechtigt"); } - boolean changed = !existing.name().equals(mutation.name().trim()) - || !safeEquals(existing.email(), blankToNull(mutation.email())); + boolean changed = !existing.companyName().equals(mutation.companyName().trim()) + || !safeEquals(existing.customerNumber(), blankToNull(mutation.customerNumber())) + || !safeEquals(existing.contactPerson(), blankToNull(mutation.contactPerson())) + || !safeEquals(existing.street(), blankToNull(mutation.street())) + || !safeEquals(existing.houseNumber(), blankToNull(mutation.houseNumber())) + || !safeEquals(existing.postalCode(), blankToNull(mutation.postalCode())) + || !safeEquals(existing.city(), blankToNull(mutation.city())) + || !safeEquals(existing.email(), blankToNull(mutation.email())) + || !safeEquals(existing.phoneNumber(), blankToNull(mutation.phoneNumber())); if (changed) { farmerRepository.save(new Farmer( existing.id(), existing.accountId(), existing.businessKey(), - existing.name(), + existing.customerNumber(), + existing.companyName(), + existing.contactPerson(), + existing.street(), + existing.houseNumber(), + existing.postalCode(), + existing.city(), existing.email(), + existing.phoneNumber(), false, existing.supersedesId(), existing.createdAt(), @@ -203,8 +220,15 @@ public class CatalogService { null, existing.accountId(), existing.businessKey(), - mutation.name().trim(), + blankToNull(mutation.customerNumber()), + mutation.companyName().trim(), + blankToNull(mutation.contactPerson()), + blankToNull(mutation.street()), + blankToNull(mutation.houseNumber()), + blankToNull(mutation.postalCode()), + blankToNull(mutation.city()), blankToNull(mutation.email()), + blankToNull(mutation.phoneNumber()), mutation.active(), existing.id(), now, @@ -217,8 +241,15 @@ public class CatalogService { existing.id(), existing.accountId(), existing.businessKey(), - existing.name(), + existing.customerNumber(), + existing.companyName(), + existing.contactPerson(), + existing.street(), + existing.houseNumber(), + existing.postalCode(), + existing.city(), existing.email(), + existing.phoneNumber(), mutation.active(), existing.supersedesId(), existing.createdAt(), @@ -746,8 +777,15 @@ public class CatalogService { return new FarmerRow( farmer.id(), farmer.businessKey(), - farmer.name(), + farmer.customerNumber(), + farmer.companyName(), + farmer.contactPerson(), + farmer.street(), + farmer.houseNumber(), + farmer.postalCode(), + farmer.city(), farmer.email(), + farmer.phoneNumber(), farmer.active(), farmer.updatedAt() ); @@ -812,7 +850,7 @@ public class CatalogService { } private FarmerOption toFarmerOption(Farmer farmer) { - return new FarmerOption(farmer.businessKey(), farmer.name(), farmer.email()); + return new FarmerOption(farmer.businessKey(), farmer.companyName(), farmer.contactPerson(), farmer.email()); } private MedicationOption toMedicationOption(MedicationCatalogItem item) { @@ -1077,20 +1115,31 @@ public class CatalogService { LocalDateTime now = LocalDateTime.now(); - // Migriere Farmers ohne accountId + // Migriere Farmers ohne accountId oder mit altem Schema farmerRepository.findAll().stream() - .filter(farmer -> isBlank(farmer.accountId())) - .forEach(farmer -> farmerRepository.save(new Farmer( - farmer.id(), - defaultAccountId, - farmer.businessKey(), - farmer.name(), - farmer.email(), - farmer.active(), - farmer.supersedesId(), - farmer.createdAt(), - now - ))); + .filter(farmer -> isBlank(farmer.accountId()) || isBlank(farmer.companyName())) + .forEach(farmer -> { + // Wenn companyName fehlt, nutze businessKey als Fallback + String companyName = isBlank(farmer.companyName()) ? farmer.businessKey() : farmer.companyName(); + farmerRepository.save(new Farmer( + farmer.id(), + isBlank(farmer.accountId()) ? defaultAccountId : farmer.accountId(), + farmer.businessKey(), + null, // customerNumber + companyName, + null, // contactPerson + null, // street + null, // houseNumber + null, // postalCode + null, // city + farmer.email(), + null, // phoneNumber + farmer.active(), + farmer.supersedesId(), + farmer.createdAt(), + now + )); + }); // Migriere Medications ohne accountId medicationRepository.findAll().stream() @@ -1213,7 +1262,7 @@ public class CatalogService { ) { } - public record FarmerOption(String businessKey, String name, String email) { + public record FarmerOption(String businessKey, String companyName, String contactPerson, String email) { } public record MedicationOption(String businessKey, String name, MedicationCategory category) { @@ -1249,14 +1298,33 @@ public class CatalogService { public record FarmerRow( String id, String businessKey, - String name, + String customerNumber, + String companyName, + String contactPerson, + String street, + String houseNumber, + String postalCode, + String city, String email, + String phoneNumber, boolean active, LocalDateTime updatedAt ) { } - public record FarmerMutation(String id, String name, String email, boolean active) { + public record FarmerMutation( + String id, + String customerNumber, + String companyName, + String contactPerson, + String street, + String houseNumber, + String postalCode, + String city, + String email, + String phoneNumber, + boolean active + ) { } public record MedicationRow( diff --git a/backend/src/main/java/de/svencarstensen/muh/service/InvoiceService.java b/backend/src/main/java/de/svencarstensen/muh/service/InvoiceService.java index 183e23e..8338633 100644 --- a/backend/src/main/java/de/svencarstensen/muh/service/InvoiceService.java +++ b/backend/src/main/java/de/svencarstensen/muh/service/InvoiceService.java @@ -14,10 +14,8 @@ import org.springframework.stereotype.Service; import org.springframework.web.server.ResponseStatusException; import java.time.LocalDate; -import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.List; -import java.util.Locale; import java.util.Optional; @Service diff --git a/backend/src/main/java/de/svencarstensen/muh/service/PortalService.java b/backend/src/main/java/de/svencarstensen/muh/service/PortalService.java index 31b6108..9adae9e 100644 --- a/backend/src/main/java/de/svencarstensen/muh/service/PortalService.java +++ b/backend/src/main/java/de/svencarstensen/muh/service/PortalService.java @@ -31,7 +31,7 @@ public class PortalService { LocalDate date ) { List matchingFarmers = catalogService.activeCatalogSummary(actorId).farmers().stream() - .filter(farmer -> farmerQuery == null || farmerQuery.isBlank() || farmer.name().toLowerCase(Locale.ROOT).contains(farmerQuery.toLowerCase(Locale.ROOT))) + .filter(farmer -> farmerQuery == null || farmerQuery.isBlank() || farmer.companyName().toLowerCase(Locale.ROOT).contains(farmerQuery.toLowerCase(Locale.ROOT))) .toList(); List sampleRows; diff --git a/backend/src/main/java/de/svencarstensen/muh/service/SampleService.java b/backend/src/main/java/de/svencarstensen/muh/service/SampleService.java index 1764283..f59e013 100644 --- a/backend/src/main/java/de/svencarstensen/muh/service/SampleService.java +++ b/backend/src/main/java/de/svencarstensen/muh/service/SampleService.java @@ -124,7 +124,7 @@ public class SampleService { null, sampleNumber, farmer.businessKey(), - farmer.name(), + farmer.companyName(), farmer.email(), request.cowNumber().trim(), blankToNull(request.cowName()), @@ -178,7 +178,7 @@ public class SampleService { existing.id(), existing.sampleNumber(), farmer.businessKey(), - farmer.name(), + farmer.companyName(), farmer.email(), request.cowNumber().trim(), blankToNull(request.cowName()), diff --git a/frontend/src/lib/types.ts b/frontend/src/lib/types.ts index eb915e3..3b44175 100644 --- a/frontend/src/lib/types.ts +++ b/frontend/src/lib/types.ts @@ -27,7 +27,8 @@ export type UserRole = "ADMIN" | "CUSTOMER"; export interface FarmerOption { businessKey: string; - name: string; + companyName: string; + contactPerson: string | null; email: string | null; } @@ -204,8 +205,15 @@ export interface SampleDetail { export interface FarmerRow { id: string; businessKey: string; - name: string; + customerNumber: string | null; + companyName: string; + contactPerson: string | null; + street: string | null; + houseNumber: string | null; + postalCode: string | null; + city: string | null; email: string | null; + phoneNumber: string | null; active: boolean; updatedAt: string; } diff --git a/frontend/src/pages/AdministrationPage.tsx b/frontend/src/pages/AdministrationPage.tsx index 353913b..cf9fce2 100644 --- a/frontend/src/pages/AdministrationPage.tsx +++ b/frontend/src/pages/AdministrationPage.tsx @@ -8,13 +8,23 @@ type DatasetKey = "farmers" | "medications" | "pathogens" | "antibiotics"; type EditableRow = { id: string; businessKey: string; - name: string; - active: boolean; - updatedAt: string; + // Farmer fields + customerNumber?: string; + companyName?: string; + contactPerson?: string; + street?: string; + houseNumber?: string; + postalCode?: string; + city?: string; email?: string; + phoneNumber?: string; + // Other fields + name?: string; category?: MedicationCategory; code?: string; kind?: PathogenKind; + active: boolean; + updatedAt: string; }; type DatasetsState = Record; @@ -33,13 +43,51 @@ const DATASET_TITLES: Record = { antibiotics: "Die Verwaltung der Antibiogramme", }; +const FARMER_REQUIRED_FIELDS: Array = [ + "companyName", + "customerNumber", + "contactPerson", + "street", + "houseNumber", + "postalCode", + "city", + "email", + "phoneNumber", +]; + +function isBlankValue(value: string | undefined) { + return !value?.trim(); +} + +function isFarmerFieldInvalid(row: EditableRow, field: keyof EditableRow, showValidation: boolean) { + if (!showValidation) { + return false; + } + const value = row[field]; + return typeof value !== "string" || isBlankValue(value); +} + +function isFarmerRowIncomplete(row: EditableRow) { + return FARMER_REQUIRED_FIELDS.some((field) => { + const value = row[field]; + return typeof value !== "string" || isBlankValue(value); + }); +} + function normalizeOverview(overview: AdministrationOverview): DatasetsState { return { farmers: overview.farmers.map((entry) => ({ id: entry.id, businessKey: entry.businessKey, - name: entry.name, + customerNumber: entry.customerNumber ?? "", + companyName: entry.companyName, + contactPerson: entry.contactPerson ?? "", + street: entry.street ?? "", + houseNumber: entry.houseNumber ?? "", + postalCode: entry.postalCode ?? "", + city: entry.city ?? "", email: entry.email ?? "", + phoneNumber: entry.phoneNumber ?? "", active: entry.active, updatedAt: entry.updatedAt, })), @@ -74,7 +122,21 @@ function normalizeOverview(overview: AdministrationOverview): DatasetsState { function emptyRow(dataset: DatasetKey): EditableRow { switch (dataset) { case "farmers": - return { id: "", businessKey: "", name: "", email: "", active: true, updatedAt: new Date().toISOString() }; + return { + id: "", + businessKey: "", + customerNumber: "", + companyName: "", + contactPerson: "", + street: "", + houseNumber: "", + postalCode: "", + city: "", + email: "", + phoneNumber: "", + active: true, + updatedAt: new Date().toISOString() + }; case "medications": return { id: "", @@ -133,6 +195,7 @@ export default function AdministrationPage() { }, []); const rows = useMemo(() => datasets?.[selectedDataset] ?? [], [datasets, selectedDataset]); + const isFarmerDataset = selectedDataset === "farmers"; function updateRow(index: number, patch: Partial) { setDatasets((current) => { @@ -166,7 +229,11 @@ export default function AdministrationPage() { return; } setShowValidation(true); - if (rows.some((row) => !row.name.trim())) { + if (selectedDataset === "farmers" && rows.some(isFarmerRowIncomplete)) { + setMessage("Bitte alle Pflichtfelder fuer den Landwirt ausfuellen."); + return; + } + if (selectedDataset !== "farmers" && rows.some((row) => !row.name?.trim())) { setMessage("Bitte alle Pflichtfelder ausfuellen."); return; } @@ -178,8 +245,15 @@ export default function AdministrationPage() { case "farmers": response = await apiPost("/catalog/farmers", rows.map((row) => ({ id: row.id || null, - name: row.name, + customerNumber: row.customerNumber || null, + companyName: row.companyName, + contactPerson: row.contactPerson || null, + street: row.street || null, + houseNumber: row.houseNumber || null, + postalCode: row.postalCode || null, + city: row.city || null, email: row.email || null, + phoneNumber: row.phoneNumber || null, active: row.active, }))); break; @@ -245,87 +319,198 @@ export default function AdministrationPage() { -
- - - - - {selectedDataset === "farmers" ? : null} - {selectedDataset === "medications" ? : null} - {selectedDataset === "pathogens" || selectedDataset === "antibiotics" ? : null} - {selectedDataset === "pathogens" ? : null} - - - - - {rows.map((row, index) => ( - - - {selectedDataset === "farmers" ? ( - - ) : null} - {selectedDataset === "medications" ? ( - - ) : null} - {selectedDataset === "pathogens" || selectedDataset === "antibiotics" ? ( - - ) : null} - {selectedDataset === "pathogens" ? ( - - ) : null} - + + + + ))} + + ) : ( +
+
NameE-MailKategorieKuerzelTypAktiv
+ {isFarmerDataset ? ( +
+ {rows.map((row, index) => ( +
+
+
- updateRow(index, { email: event.target.value })} - /> - - - - updateRow(index, { code: event.target.value })} - /> - - - + + + + + +
+ + + + +
+ +
+ + +
+ +
+
+ + + + {selectedDataset === "medications" ? : null} + {selectedDataset === "pathogens" || selectedDataset === "antibiotics" ? : null} + {selectedDataset === "pathogens" ? : null} + - ))} - -
NameKategorieKuerzelTypAktiv
-
+ + + {rows.map((row, index) => ( + + + updateRow(index, { name: event.target.value })} + /> + + {selectedDataset === "medications" ? ( + + + + ) : null} + {selectedDataset === "pathogens" || selectedDataset === "antibiotics" ? ( + + updateRow(index, { code: event.target.value })} + /> + + ) : null} + {selectedDataset === "pathogens" ? ( + + + + ) : null} + + + + + ))} + + + + )}
))} diff --git a/frontend/src/styles/global.css b/frontend/src/styles/global.css index 4bd2ec7..9c87fc6 100644 --- a/frontend/src/styles/global.css +++ b/frontend/src/styles/global.css @@ -570,6 +570,46 @@ a { overflow: auto; } +.admin-farmer-list { + display: grid; + gap: 20px; +} + +.admin-farmer-card { + display: grid; + gap: 16px; + padding: 24px; + border: 1px solid rgba(37, 49, 58, 0.08); + border-radius: 24px; + background: rgba(255, 255, 255, 0.42); +} + +.admin-farmer-card__row { + display: grid; + gap: 16px; +} + +.admin-farmer-card__row--primary { + grid-template-columns: minmax(0, 1.8fr) minmax(0, 1.3fr) minmax(0, 1fr); +} + +.admin-farmer-card__row--address { + grid-template-columns: minmax(0, 0.75fr) minmax(0, 1.05fr) minmax(0, 1.8fr) minmax(0, 0.9fr); +} + +.admin-farmer-card__row--contact { + grid-template-columns: minmax(0, 1.5fr) minmax(0, 1.1fr); +} + +.admin-farmer-card__row--toggle { + grid-template-columns: minmax(0, 180px); +} + +.admin-farmer-card__toggle { + min-width: 140px; + justify-content: center; +} + .data-table { width: 100%; border-collapse: collapse; @@ -590,6 +630,35 @@ a { text-transform: uppercase; } +@media (max-width: 1200px) { + .admin-farmer-card__row--primary, + .admin-farmer-card__row--address, + .admin-farmer-card__row--contact { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .admin-farmer-card__row--toggle { + grid-template-columns: minmax(0, 180px); + } + + .admin-farmer-card__toggle { + width: 100%; + } +} + +@media (max-width: 720px) { + .admin-farmer-card { + padding: 18px; + } + + .admin-farmer-card__row--primary, + .admin-farmer-card__row--address, + .admin-farmer-card__row--contact, + .admin-farmer-card__row--toggle { + grid-template-columns: 1fr; + } +} + .status-pill, .info-chip { display: inline-flex;