Add Docker support and enhance data models for tender details
- Introduced a .dockerignore file to optimize Docker builds by excluding unnecessary files. - Updated the Dockerfile to use a Flutter base image, enabling web builds for the application. - Added new data models for `Lot` and `OrganizationAddress` to represent tender details more accurately. - Enhanced the `Organization` model to include `companyId` and a nested `address` field. - Updated the `TenderData` model to include new fields such as `noticeTypeCode`, `formType`, and `lots`, reflecting the API response structure. - Regenerated necessary code for data models to ensure compatibility with the updated structures.
This commit is contained in:
@@ -1,10 +1,21 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tm_app/core/theme/colors.dart';
|
||||
import 'package:tm_app/core/utils/date_utils.dart';
|
||||
import 'package:tm_app/core/utils/price_extension.dart';
|
||||
import 'package:tm_app/core/utils/size_config.dart';
|
||||
import 'package:tm_app/data/services/model/tender_data/tender_data.dart';
|
||||
import 'package:tm_app/views/detail/strings/tender_details_strings.dart';
|
||||
import 'package:tm_app/views/detail/widgets/deadline_item.dart';
|
||||
import 'package:tm_app/views/detail/widgets/info_item.dart';
|
||||
|
||||
// AI-NOTE (for PR reviewer): This widget was rewritten to render ONLY the
|
||||
// backend-driven fields requested for the tender-details page (notice type,
|
||||
// form type, languages, issue/publication dates, procedure, value, duration,
|
||||
// deadlines, buyer org/address, lots, status, ids). Design is unchanged — it
|
||||
// still uses InfoItem/DeadlineItem. The _infoItem() helper returns an empty
|
||||
// list when a value is null/blank so empty fields don't render as blank rows
|
||||
// (this is why each entry is spread with `...`). The old hard-coded
|
||||
// "Reference Number" row was dropped because it isn't in the requested set.
|
||||
class TenderDetailInfoSection extends StatelessWidget {
|
||||
final bool isScreenBig;
|
||||
final TenderData detail;
|
||||
@@ -17,24 +28,114 @@ class TenderDetailInfoSection extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final buyer = detail.buyerOrganization;
|
||||
final address = buyer?.address;
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
DeadlineItem(detail: detail, isScreenBig: isScreenBig),
|
||||
InfoItem(
|
||||
title: TenderDetailsStrings.tenderClientLabel,
|
||||
value: detail.buyerOrganization!.name ?? '',
|
||||
..._infoItem(TenderDetailsStrings.noticeTypeCode, detail.noticeTypeCode),
|
||||
..._infoItem(TenderDetailsStrings.formType, detail.formType),
|
||||
..._infoItem(
|
||||
TenderDetailsStrings.noticeLanguageCode,
|
||||
detail.noticeLanguageCode,
|
||||
),
|
||||
InfoItem(
|
||||
title: TenderDetailsStrings.estimatedValue,
|
||||
value:
|
||||
'${detail.estimatedValue?.formattedPrice} ${detail.currency ?? ''}',
|
||||
..._infoItem(
|
||||
TenderDetailsStrings.issueDate,
|
||||
_date(detail.issueDate),
|
||||
),
|
||||
InfoItem(
|
||||
title: TenderDetailsStrings.tenderReferenceNumberLabel,
|
||||
value: detail.noticePublicationId ?? '',
|
||||
..._infoItem(
|
||||
TenderDetailsStrings.issueTime,
|
||||
_dateTime(detail.issueTime),
|
||||
),
|
||||
..._infoItem(TenderDetailsStrings.procedureCode, detail.procedureCode),
|
||||
..._infoItem(
|
||||
TenderDetailsStrings.estimatedValue,
|
||||
detail.estimatedValue == null
|
||||
? null
|
||||
: '${detail.estimatedValue?.formattedPrice} ${detail.currency ?? ''}',
|
||||
),
|
||||
..._infoItem(TenderDetailsStrings.durationLabel, _duration()),
|
||||
..._infoItem(
|
||||
TenderDetailsStrings.publicationDate,
|
||||
_date(detail.publicationDate),
|
||||
),
|
||||
..._infoItem(
|
||||
TenderDetailsStrings.tenderDeadlineDate,
|
||||
_date(detail.tenderDeadline),
|
||||
),
|
||||
..._infoItem(TenderDetailsStrings.countryCode, detail.countryCode),
|
||||
..._infoItem(TenderDetailsStrings.buyerName, buyer?.name),
|
||||
..._infoItem(TenderDetailsStrings.companyId, buyer?.companyId),
|
||||
..._infoItem(TenderDetailsStrings.buyerCity, address?.cityName),
|
||||
..._infoItem(TenderDetailsStrings.buyerCountry, address?.countryCode),
|
||||
..._infoItem(
|
||||
TenderDetailsStrings.officialLanguages,
|
||||
(detail.officialLanguages == null ||
|
||||
detail.officialLanguages!.isEmpty)
|
||||
? null
|
||||
: detail.officialLanguages!.join(', '),
|
||||
),
|
||||
..._infoItem(TenderDetailsStrings.status, detail.status),
|
||||
..._infoItem(TenderDetailsStrings.tenderId, detail.tenderId),
|
||||
..._lotsSection(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns an [InfoItem] only when [value] has content, so empty fields are
|
||||
/// not rendered as blank rows.
|
||||
List<Widget> _infoItem(String title, String? value) {
|
||||
if (value == null || value.trim().isEmpty) {
|
||||
return const [];
|
||||
}
|
||||
return [InfoItem(title: title, value: value)];
|
||||
}
|
||||
|
||||
List<Widget> _lotsSection() {
|
||||
final lots = detail.lots;
|
||||
if (lots == null || lots.isEmpty) {
|
||||
return const [];
|
||||
}
|
||||
|
||||
return [
|
||||
SizedBox(height: 8.0.h()),
|
||||
Text(
|
||||
TenderDetailsStrings.lots,
|
||||
style: TextStyle(
|
||||
color: AppColors.grey80,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 16.0.sp(),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8.0.h()),
|
||||
for (final lot in lots) ...[
|
||||
..._infoItem(TenderDetailsStrings.lotId, lot.lotId),
|
||||
..._infoItem('Title', lot.title),
|
||||
..._infoItem('Description', lot.description),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
String? _duration() {
|
||||
final duration = detail.duration?.trim() ?? '';
|
||||
final unit = detail.durationUnit?.trim() ?? '';
|
||||
final combined = '$duration $unit'.trim();
|
||||
return combined.isEmpty ? null : combined;
|
||||
}
|
||||
|
||||
String? _date(int? unix) {
|
||||
if (unix == null) {
|
||||
return null;
|
||||
}
|
||||
return timeConvertor(unix);
|
||||
}
|
||||
|
||||
String? _dateTime(int? unix) {
|
||||
if (unix == null) {
|
||||
return null;
|
||||
}
|
||||
return timeConvertor(unix, hasTime: true);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user