added download from excel and zip in frontend
This commit is contained in:
@@ -16,6 +16,7 @@ import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
|||||||
|
|
||||||
import marketing.heyday.hartmann.fotodocumentation.core.model.Questionnaire;
|
import marketing.heyday.hartmann.fotodocumentation.core.model.Questionnaire;
|
||||||
import marketing.heyday.hartmann.fotodocumentation.core.model.QuestionnaireCustomer;
|
import marketing.heyday.hartmann.fotodocumentation.core.model.QuestionnaireCustomer;
|
||||||
|
import marketing.heyday.hartmann.fotodocumentation.core.utils.QuestionnaireJsonParser.MatrixAnswer;
|
||||||
import marketing.heyday.hartmann.fotodocumentation.core.utils.QuestionnaireJsonParser.QuestionJsonObj;
|
import marketing.heyday.hartmann.fotodocumentation.core.utils.QuestionnaireJsonParser.QuestionJsonObj;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,7 +34,9 @@ public class ExcelUtils {
|
|||||||
|
|
||||||
public Optional<byte[]> create(QuestionnaireCustomer customer, List<Questionnaire> questionnaires) {
|
public Optional<byte[]> create(QuestionnaireCustomer customer, List<Questionnaire> questionnaires) {
|
||||||
LOG.debug("Create excel file for customer " + customer);
|
LOG.debug("Create excel file for customer " + customer);
|
||||||
// TODO: implement excel export
|
if(customer == null || questionnaires.isEmpty()) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); XSSFWorkbook workbook = new XSSFWorkbook()) {
|
try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); XSSFWorkbook workbook = new XSSFWorkbook()) {
|
||||||
|
|
||||||
@@ -176,12 +179,7 @@ public class ExcelUtils {
|
|||||||
if (!answer.selected()) {
|
if (!answer.selected()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int index = 0;
|
int index = getMatrixAnswerIndex(answerOpts, answer);
|
||||||
for (int i = 0; i < answerOpts.size(); i++) {
|
|
||||||
if (answer.answer().equalsIgnoreCase(answerOpts.get(i).answer())) {
|
|
||||||
index = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var cell = questionRow.createCell(1 + index);
|
var cell = questionRow.createCell(1 + index);
|
||||||
cell.setCellType(CellType.BOOLEAN);
|
cell.setCellType(CellType.BOOLEAN);
|
||||||
cell.setCellValue(answer.selected());
|
cell.setCellValue(answer.selected());
|
||||||
@@ -190,4 +188,14 @@ public class ExcelUtils {
|
|||||||
}
|
}
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int getMatrixAnswerIndex(List<MatrixAnswer> answerOpts, MatrixAnswer answer) {
|
||||||
|
int index = 0;
|
||||||
|
for (int i = 0; i < answerOpts.size(); i++) {
|
||||||
|
if (answer.answer().equalsIgnoreCase(answerOpts.get(i).answer())) {
|
||||||
|
index = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return index;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,17 +29,17 @@ import com.drew.metadata.exif.ExifIFD0Directory;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
public interface ImageHandler {
|
public interface ImageHandler {
|
||||||
static final Log LOG = LogFactory.getLog(ImageHandler.class);
|
Log LOG = LogFactory.getLog(ImageHandler.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads image bytes and returns a BufferedImage with correct EXIF orientation applied.
|
* Reads image bytes and returns a BufferedImage with correct EXIF orientation applied.
|
||||||
*/
|
*/
|
||||||
default BufferedImage readImageWithCorrectOrientation(byte[] imageBytes) throws IOException {
|
default BufferedImage readImageWithCorrectOrientation(byte[] imageBytes) throws IOException {
|
||||||
int orientation = getExifOrientation(imageBytes);
|
|
||||||
BufferedImage image = ImageIO.read(new ByteArrayInputStream(imageBytes));
|
BufferedImage image = ImageIO.read(new ByteArrayInputStream(imageBytes));
|
||||||
if (image == null) {
|
if (image == null) {
|
||||||
throw new IOException("Failed to read image from byte array");
|
throw new IOException("Failed to read image from byte array");
|
||||||
}
|
}
|
||||||
|
int orientation = getExifOrientation(imageBytes);
|
||||||
return applyOrientation(image, orientation);
|
return applyOrientation(image, orientation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -228,7 +228,9 @@ public class PdfUtils implements ImageHandler {
|
|||||||
// Return remaining text starting from current word
|
// Return remaining text starting from current word
|
||||||
StringBuilder remaining = new StringBuilder(line);
|
StringBuilder remaining = new StringBuilder(line);
|
||||||
for (int i = wordIndex; i < words.length; i++) {
|
for (int i = wordIndex; i < words.length; i++) {
|
||||||
if (!remaining.isEmpty()) remaining.append(" ");
|
if (!remaining.isEmpty()) {
|
||||||
|
remaining.append(" ");
|
||||||
|
}
|
||||||
remaining.append(words[i]);
|
remaining.append(words[i]);
|
||||||
}
|
}
|
||||||
return remaining.toString();
|
return remaining.toString();
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import org.apache.commons.logging.Log;
|
|||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
import jakarta.json.*;
|
import jakarta.json.*;
|
||||||
|
import jakarta.json.JsonValue.ValueType;
|
||||||
import jakarta.json.stream.JsonParsingException;
|
import jakarta.json.stream.JsonParsingException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -74,10 +75,10 @@ public class QuestionnaireJsonParser {
|
|||||||
public record QuestionJsonObj(String id, String title, int order, String type, String data) {
|
public record QuestionJsonObj(String id, String title, int order, String type, String data) {
|
||||||
|
|
||||||
public List<MatrixQuestion> getMatrixAnswer() {
|
public List<MatrixQuestion> getMatrixAnswer() {
|
||||||
return getValue("questions", () -> null, (questions) -> {
|
return getValue("questions", () -> null, matrixQuestions -> {
|
||||||
var retVal = new ArrayList<MatrixQuestion>();
|
var retVal = new ArrayList<MatrixQuestion>();
|
||||||
|
|
||||||
for (var question : questions) {
|
for (var question : matrixQuestions) {
|
||||||
var questionObj = question.asJsonObject();
|
var questionObj = question.asJsonObject();
|
||||||
var id = questionObj.getString("id");
|
var id = questionObj.getString("id");
|
||||||
var title = questionObj.getString("title");
|
var title = questionObj.getString("title");
|
||||||
@@ -101,11 +102,15 @@ public class QuestionnaireJsonParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Integer getNumberAnswer() {
|
public Integer getNumberAnswer() {
|
||||||
return getValue(() -> null, (answers) -> {
|
return getValue(() -> null, answers -> {
|
||||||
for (var answer : answers) {
|
for (var answer : answers) {
|
||||||
var answerObj = answer.asJsonObject();
|
var answerObj = answer.asJsonObject();
|
||||||
if (answerObj.getBoolean("selected")) {
|
if (answerObj.getBoolean("selected")) {
|
||||||
return answerObj.getInt("answer");
|
var value = answerObj.get("answer");
|
||||||
|
if (value.getValueType() == ValueType.NUMBER) {
|
||||||
|
return answerObj.getInt("answer");
|
||||||
|
}
|
||||||
|
return Integer.parseInt(answerObj.getString("answer"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -113,7 +118,7 @@ public class QuestionnaireJsonParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getSingleAnswer() {
|
public String getSingleAnswer() {
|
||||||
return getValue(() -> "", (answers) -> {
|
return getValue(() -> "", answers -> {
|
||||||
for (var answer : answers) {
|
for (var answer : answers) {
|
||||||
var answerObj = answer.asJsonObject();
|
var answerObj = answer.asJsonObject();
|
||||||
if (answerObj.getBoolean("selected")) {
|
if (answerObj.getBoolean("selected")) {
|
||||||
@@ -125,7 +130,7 @@ public class QuestionnaireJsonParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getMultiAnswer() {
|
public List<String> getMultiAnswer() {
|
||||||
return getValue(() -> List.of(), (answers) -> {
|
return getValue(() -> List.of(), answers -> {
|
||||||
List<String> retVal = new ArrayList<>();
|
List<String> retVal = new ArrayList<>();
|
||||||
for (var answer : answers) {
|
for (var answer : answers) {
|
||||||
var answerObj = answer.asJsonObject();
|
var answerObj = answer.asJsonObject();
|
||||||
|
|||||||
@@ -246,7 +246,7 @@ class ExcelUtilsTest implements TestAble {
|
|||||||
private Questionnaire createQuestionnaire(Date date, String questions) {
|
private Questionnaire createQuestionnaire(Date date, String questions) {
|
||||||
return new Questionnaire.Builder()
|
return new Questionnaire.Builder()
|
||||||
.questionnaireDate(date)
|
.questionnaireDate(date)
|
||||||
.questions(QuestionnaireJsonParserTest.testJson1)
|
.questions(QuestionnaireJsonParserTest.TEST_JSON_1)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,14 +21,14 @@ public class QuestionnaireJsonParserTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testJson1() {
|
public void testJson1() {
|
||||||
var parser = new QuestionnaireJsonParser();
|
var parser = new QuestionnaireJsonParser();
|
||||||
boolean retVal = parser.parse(testJson1);
|
boolean retVal = parser.parse(TEST_JSON_1);
|
||||||
assertTrue(retVal);
|
assertTrue(retVal);
|
||||||
var questions = parser.getQuestions();
|
var questions = parser.getQuestions();
|
||||||
assertEquals(10, questions.size());
|
assertEquals(10, questions.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static final String testJson1 = """
|
public static final String TEST_JSON_1 = """
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"id": "question1",
|
"id": "question1",
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 506 B |
@@ -7,6 +7,8 @@ abstract interface class QuestionnaireCustomerController {
|
|||||||
Future<QuestionnaireCustomerDto?> get({required int id});
|
Future<QuestionnaireCustomerDto?> get({required int id});
|
||||||
|
|
||||||
Future<List<int>> export({required int customerId, int? questionnaireId});
|
Future<List<int>> export({required int customerId, int? questionnaireId});
|
||||||
|
|
||||||
|
Future<List<int>> exportAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
class QuestionnaireCustomerControllerImpl extends BaseController implements QuestionnaireCustomerController {
|
class QuestionnaireCustomerControllerImpl extends BaseController implements QuestionnaireCustomerController {
|
||||||
@@ -39,4 +41,10 @@ class QuestionnaireCustomerControllerImpl extends BaseController implements Ques
|
|||||||
}
|
}
|
||||||
return runGetBytesWithAuth(uriStr);
|
return runGetBytesWithAuth(uriStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<int>> exportAll() {
|
||||||
|
String uriStr = '${uriUtils.getBaseUrl()}$path/exportall';
|
||||||
|
return runGetBytesWithAuth(uriStr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import 'package:fotodocumentation/dto/picture_dto.dart';
|
|||||||
import 'package:fotodocumentation/l10n/app_localizations.dart';
|
import 'package:fotodocumentation/l10n/app_localizations.dart';
|
||||||
import 'package:fotodocumentation/pages/ui_utils/component/customer_back_button.dart';
|
import 'package:fotodocumentation/pages/ui_utils/component/customer_back_button.dart';
|
||||||
import 'package:fotodocumentation/pages/foto/customer/foto_picture_fullscreen_dialog.dart';
|
import 'package:fotodocumentation/pages/foto/customer/foto_picture_fullscreen_dialog.dart';
|
||||||
|
import 'package:fotodocumentation/pages/ui_utils/component/evaluation_circle.dart';
|
||||||
import 'package:fotodocumentation/pages/ui_utils/component/general_error_widget.dart';
|
import 'package:fotodocumentation/pages/ui_utils/component/general_error_widget.dart';
|
||||||
import 'package:fotodocumentation/pages/ui_utils/component/page_header_widget.dart';
|
import 'package:fotodocumentation/pages/ui_utils/component/page_header_widget.dart';
|
||||||
import 'package:fotodocumentation/pages/ui_utils/component/waiting_widget.dart';
|
import 'package:fotodocumentation/pages/ui_utils/component/waiting_widget.dart';
|
||||||
@@ -306,25 +307,28 @@ class _FotoPictureWidgetState extends State<FotoPictureWidget> {
|
|||||||
Row(
|
Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
_evaluationCircle(
|
EvaluationCircleWidget(
|
||||||
key: const Key("evaluation_good"),
|
buttonKey: const Key("evaluation_good"),
|
||||||
color: _generalStyle.evaluationGoodColor,
|
color: _generalStyle.evaluationGoodColor,
|
||||||
value: 1,
|
value: 1,
|
||||||
selected: dto.evaluation == 1,
|
selected: dto.evaluation == 1,
|
||||||
|
doUpdate: (circle) => _actionUpdateEvaluation(circle.value),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
_evaluationCircle(
|
EvaluationCircleWidget(
|
||||||
key: const Key("evaluation_middle"),
|
buttonKey: const Key("evaluation_middle"),
|
||||||
color: _generalStyle.evaluationMiddleColor,
|
color: _generalStyle.evaluationMiddleColor,
|
||||||
value: 2,
|
value: 2,
|
||||||
selected: dto.evaluation == 2,
|
selected: dto.evaluation == 2,
|
||||||
|
doUpdate: (circle) => _actionUpdateEvaluation(circle.value),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
_evaluationCircle(
|
EvaluationCircleWidget(
|
||||||
key: const Key("evaluation_bad"),
|
buttonKey: const Key("evaluation_bad"),
|
||||||
color: _generalStyle.evaluationBadColor,
|
color: _generalStyle.evaluationBadColor,
|
||||||
value: 3,
|
value: 3,
|
||||||
selected: dto.evaluation == 3,
|
selected: dto.evaluation == 3,
|
||||||
|
doUpdate: (circle) => _actionUpdateEvaluation(circle.value),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -334,28 +338,6 @@ class _FotoPictureWidgetState extends State<FotoPictureWidget> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _evaluationCircle({
|
|
||||||
required Key key,
|
|
||||||
required Color color,
|
|
||||||
required int value,
|
|
||||||
required bool selected,
|
|
||||||
}) {
|
|
||||||
return InkWell(
|
|
||||||
key: key,
|
|
||||||
onTap: () async => _actionUpdateEvaluation(value),
|
|
||||||
customBorder: const CircleBorder(),
|
|
||||||
child: Container(
|
|
||||||
width: 24,
|
|
||||||
height: 24,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: color,
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
border: selected ? Border.all(color: _generalStyle.secondaryWidgetBackgroundColor, width: 3) : null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bottom navigation buttons
|
// Bottom navigation buttons
|
||||||
Widget _bottomNavigationWidget(List<PictureDto> pictures, PictureDto selectedPicture) {
|
Widget _bottomNavigationWidget(List<PictureDto> pictures, PictureDto selectedPicture) {
|
||||||
pictures.sort((a, b) => a.pictureDate.compareTo(b.pictureDate));
|
pictures.sort((a, b) => a.pictureDate.compareTo(b.pictureDate));
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import 'package:fotodocumentation/pages/ui_utils/component/search_bar_widget.dar
|
|||||||
import 'package:fotodocumentation/pages/ui_utils/component/waiting_widget.dart';
|
import 'package:fotodocumentation/pages/ui_utils/component/waiting_widget.dart';
|
||||||
import 'package:fotodocumentation/pages/ui_utils/general_style.dart';
|
import 'package:fotodocumentation/pages/ui_utils/general_style.dart';
|
||||||
import 'package:fotodocumentation/utils/di_container.dart';
|
import 'package:fotodocumentation/utils/di_container.dart';
|
||||||
|
import 'package:fotodocumentation/utils/file_download.dart';
|
||||||
import 'package:fotodocumentation/utils/global_router.dart';
|
import 'package:fotodocumentation/utils/global_router.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
@@ -60,7 +61,6 @@ class _QuestionaireCustomerListWidgetState extends State<QuestionaireCustomerLis
|
|||||||
children: [
|
children: [
|
||||||
PageHeaderWidget(text: "FRAGENBOGEN AUSWERTUNG"),
|
PageHeaderWidget(text: "FRAGENBOGEN AUSWERTUNG"),
|
||||||
//_abcHeaderBar(),
|
//_abcHeaderBar(),
|
||||||
const SizedBox(width: 48),
|
|
||||||
SearchBarWidget(
|
SearchBarWidget(
|
||||||
searchController: _searchController,
|
searchController: _searchController,
|
||||||
onSearch: (text) async => actionSearch(text),
|
onSearch: (text) async => actionSearch(text),
|
||||||
@@ -68,6 +68,16 @@ class _QuestionaireCustomerListWidgetState extends State<QuestionaireCustomerLis
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: _customerListWidget(),
|
child: _customerListWidget(),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top:24.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
_downloadButton(context),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -237,6 +247,34 @@ class _QuestionaireCustomerListWidgetState extends State<QuestionaireCustomerLis
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _downloadButton(BuildContext context) {
|
||||||
|
return ElevatedButton.icon(
|
||||||
|
key: Key("download_all_button"),
|
||||||
|
onPressed: () => _actionDownload(context),
|
||||||
|
iconAlignment: IconAlignment.end,
|
||||||
|
icon: Icon(
|
||||||
|
Icons.file_download_outlined,
|
||||||
|
color: _generalStyle.primaryButtonTextColor,
|
||||||
|
size: 24,
|
||||||
|
),
|
||||||
|
label: Text(
|
||||||
|
"Alle herunterladen",
|
||||||
|
style: TextStyle(
|
||||||
|
fontFamily: _generalStyle.fontFamily,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 16,
|
||||||
|
color: _generalStyle.primaryButtonTextColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: _generalStyle.secondaryWidgetBackgroundColor,
|
||||||
|
elevation: 0,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
|
||||||
|
shape: const StadiumBorder(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> actionSearch(String text) async {
|
Future<void> actionSearch(String text) async {
|
||||||
_reloadData();
|
_reloadData();
|
||||||
}
|
}
|
||||||
@@ -250,6 +288,47 @@ class _QuestionaireCustomerListWidgetState extends State<QuestionaireCustomerLis
|
|||||||
_dtos = _questionnaireCustomerController.getAll(_searchController.text, _selectedLetter ?? "");
|
_dtos = _questionnaireCustomerController.getAll(_searchController.text, _selectedLetter ?? "");
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _actionDownload(BuildContext context) async {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: false,
|
||||||
|
builder: (context) => Dialog(
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(24.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const CircularProgressIndicator(),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
Text(
|
||||||
|
AppLocalizations.of(context)!.customerWidgetDownloadInProgress,
|
||||||
|
style: TextStyle(
|
||||||
|
fontFamily: _generalStyle.fontFamily,
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
final bytes = await _questionnaireCustomerController.exportAll();
|
||||||
|
final fileName = 'download.zip';
|
||||||
|
if (context.mounted) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
await downloadFile(bytes, fileName);
|
||||||
|
} catch (e) {
|
||||||
|
if (context.mounted) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
Widget _abcHeaderBar() {
|
Widget _abcHeaderBar() {
|
||||||
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import 'package:fotodocumentation/dto/questionnaire_dto.dart';
|
|||||||
import 'package:fotodocumentation/l10n/app_localizations.dart';
|
import 'package:fotodocumentation/l10n/app_localizations.dart';
|
||||||
import 'package:fotodocumentation/pages/questionnaire/customer/questionnaire_delete_dialog.dart';
|
import 'package:fotodocumentation/pages/questionnaire/customer/questionnaire_delete_dialog.dart';
|
||||||
import 'package:fotodocumentation/pages/ui_utils/component/customer_back_button.dart';
|
import 'package:fotodocumentation/pages/ui_utils/component/customer_back_button.dart';
|
||||||
|
import 'package:fotodocumentation/pages/ui_utils/component/evaluation_circle.dart';
|
||||||
import 'package:fotodocumentation/pages/ui_utils/component/general_error_widget.dart';
|
import 'package:fotodocumentation/pages/ui_utils/component/general_error_widget.dart';
|
||||||
import 'package:fotodocumentation/pages/ui_utils/component/page_header_widget.dart';
|
import 'package:fotodocumentation/pages/ui_utils/component/page_header_widget.dart';
|
||||||
import 'package:fotodocumentation/pages/ui_utils/component/waiting_widget.dart';
|
import 'package:fotodocumentation/pages/ui_utils/component/waiting_widget.dart';
|
||||||
@@ -26,7 +27,7 @@ class QuestionaireCustomerWidget extends StatefulWidget {
|
|||||||
|
|
||||||
class _QuestionaireCustomerWidgetState extends State<QuestionaireCustomerWidget> {
|
class _QuestionaireCustomerWidgetState extends State<QuestionaireCustomerWidget> {
|
||||||
QuestionnaireCustomerController get _customerController => DiContainer.get();
|
QuestionnaireCustomerController get _customerController => DiContainer.get();
|
||||||
QuestionnaireController get _pictureController => DiContainer.get();
|
QuestionnaireController get _questionnaireController => DiContainer.get();
|
||||||
GeneralStyle get _generalStyle => DiContainer.get();
|
GeneralStyle get _generalStyle => DiContainer.get();
|
||||||
|
|
||||||
late Future<QuestionnaireCustomerDto?> _dto;
|
late Future<QuestionnaireCustomerDto?> _dto;
|
||||||
@@ -155,16 +156,7 @@ class _QuestionaireCustomerWidgetState extends State<QuestionaireCustomerWidget>
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
spacing: 8.0,
|
spacing: 8.0,
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
const SizedBox(width: 45),
|
||||||
flex: 1,
|
|
||||||
child: Align(
|
|
||||||
alignment: Alignment.centerLeft,
|
|
||||||
child: Text(
|
|
||||||
AppLocalizations.of(context)!.customerWidgetHeaderFoto,
|
|
||||||
style: headerStyle,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 3,
|
flex: 3,
|
||||||
child: Align(
|
child: Align(
|
||||||
@@ -209,7 +201,7 @@ class _QuestionaireCustomerWidgetState extends State<QuestionaireCustomerWidget>
|
|||||||
);
|
);
|
||||||
|
|
||||||
final dateStr = _dateFormat.format(questionnaireDto.questionnaireDate);
|
final dateStr = _dateFormat.format(questionnaireDto.questionnaireDate);
|
||||||
final evaluationColor = _generalStyle.evaluationColor(value: questionnaireDto.evaluation);
|
|
||||||
return InkWell(
|
return InkWell(
|
||||||
key: Key("table_row_${customerDto.id}"),
|
key: Key("table_row_${customerDto.id}"),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
@@ -219,23 +211,9 @@ class _QuestionaireCustomerWidgetState extends State<QuestionaireCustomerWidget>
|
|||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
spacing: 8.0,
|
spacing: 8.0,
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
ConstrainedBox(
|
||||||
flex: 1,
|
constraints: const BoxConstraints(minWidth: 45, maxWidth: 45),
|
||||||
child: Align(
|
child: Icon(IconData(0xE800, fontFamily: "MyFlutterApp")),
|
||||||
alignment: Alignment.centerLeft,
|
|
||||||
child: ConstrainedBox(
|
|
||||||
constraints: const BoxConstraints(maxWidth: 70, maxHeight: 70),
|
|
||||||
child:
|
|
||||||
Icon(Icons.abc),
|
|
||||||
/*
|
|
||||||
FIXME: remove me
|
|
||||||
Image.network(
|
|
||||||
headers: {cred.name: cred.value},
|
|
||||||
pictureDto.thumbnailSizeUrl,
|
|
||||||
fit: BoxFit.contain,
|
|
||||||
),*/
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 3,
|
flex: 3,
|
||||||
@@ -248,14 +226,7 @@ class _QuestionaireCustomerWidgetState extends State<QuestionaireCustomerWidget>
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: Container(
|
child: _evalationWidget(questionnaireDto),
|
||||||
width: 20,
|
|
||||||
height: 20,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: evaluationColor,
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
@@ -293,6 +264,37 @@ class _QuestionaireCustomerWidgetState extends State<QuestionaireCustomerWidget>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _evalationWidget(QuestionnaireDto dto) {
|
||||||
|
return Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
EvaluationCircleWidget(
|
||||||
|
buttonKey: const Key("evaluation_good"),
|
||||||
|
color: _generalStyle.evaluationGoodColor,
|
||||||
|
value: 1,
|
||||||
|
selected: dto.evaluation == 1,
|
||||||
|
doUpdate: (circle) async => _actionUpdateEvaluation(dto, circle.value),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
EvaluationCircleWidget(
|
||||||
|
buttonKey: const Key("evaluation_middle"),
|
||||||
|
color: _generalStyle.evaluationMiddleColor,
|
||||||
|
value: 2,
|
||||||
|
selected: dto.evaluation == 2,
|
||||||
|
doUpdate: (circle) async => _actionUpdateEvaluation(dto, circle.value),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
EvaluationCircleWidget(
|
||||||
|
buttonKey: const Key("evaluation_bad"),
|
||||||
|
color: _generalStyle.evaluationBadColor,
|
||||||
|
value: 3,
|
||||||
|
selected: dto.evaluation == 3,
|
||||||
|
doUpdate: (circle) async => _actionUpdateEvaluation(dto, circle.value),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _downloadButton(BuildContext context, QuestionnaireCustomerDto customerDto) {
|
Widget _downloadButton(BuildContext context, QuestionnaireCustomerDto customerDto) {
|
||||||
return ElevatedButton.icon(
|
return ElevatedButton.icon(
|
||||||
key: Key("download_all_button"),
|
key: Key("download_all_button"),
|
||||||
@@ -321,6 +323,12 @@ class _QuestionaireCustomerWidgetState extends State<QuestionaireCustomerWidget>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _actionUpdateEvaluation(QuestionnaireDto dto, int value) async {
|
||||||
|
dto.evaluation = value;
|
||||||
|
_questionnaireController.updateEvaluation(dto);
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _actionDelete(BuildContext context, QuestionnaireCustomerDto customerDto, QuestionnaireDto questionnaireDto) async {
|
Future<void> _actionDelete(BuildContext context, QuestionnaireCustomerDto customerDto, QuestionnaireDto questionnaireDto) async {
|
||||||
final confirmed = await showDialog<bool>(
|
final confirmed = await showDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
@@ -330,7 +338,7 @@ class _QuestionaireCustomerWidgetState extends State<QuestionaireCustomerWidget>
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (confirmed == true) {
|
if (confirmed == true) {
|
||||||
_pictureController.delete(questionnaireDto);
|
_questionnaireController.delete(questionnaireDto);
|
||||||
setState(() {
|
setState(() {
|
||||||
_dto = _customerController.get(id: widget.customerId);
|
_dto = _customerController.get(id: widget.customerId);
|
||||||
});
|
});
|
||||||
@@ -365,7 +373,7 @@ class _QuestionaireCustomerWidgetState extends State<QuestionaireCustomerWidget>
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
final bytes = await _customerController.export(customerId: customerDto.id, questionnaireId: questionnaireDto?.id);
|
final bytes = await _customerController.export(customerId: customerDto.id, questionnaireId: questionnaireDto?.id);
|
||||||
final fileName = questionnaireDto != null ? '${customerDto.customerNumber}_${questionnaireDto.id}.pdf' : '${customerDto.customerNumber}.pdf';
|
final fileName = questionnaireDto != null ? '${customerDto.customerNumber}_${questionnaireDto.id}.xlsx' : '${customerDto.customerNumber}.xlsx';
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:fotodocumentation/pages/ui_utils/general_style.dart';
|
||||||
|
import 'package:fotodocumentation/utils/di_container.dart';
|
||||||
|
|
||||||
|
class EvaluationCircleWidget extends StatelessWidget {
|
||||||
|
GeneralStyle get _generalStyle => DiContainer.get();
|
||||||
|
|
||||||
|
final Key buttonKey;
|
||||||
|
final Color color;
|
||||||
|
final int value;
|
||||||
|
final bool selected;
|
||||||
|
final Future<void> Function(EvaluationCircleWidget) doUpdate;
|
||||||
|
|
||||||
|
const EvaluationCircleWidget({super.key, required this.buttonKey, required this.color, required this.value, required this.selected, required this.doUpdate});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return InkWell(
|
||||||
|
key: buttonKey,
|
||||||
|
onTap: () async => doUpdate(this), //_actionUpdateEvaluation(value),
|
||||||
|
customBorder: const CircleBorder(),
|
||||||
|
child: Container(
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: color,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
border: selected ? Border.all(color: _generalStyle.secondaryWidgetBackgroundColor, width: 3) : null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,8 +17,8 @@ class GlobalRouter {
|
|||||||
|
|
||||||
static final String pathRoot = "/";
|
static final String pathRoot = "/";
|
||||||
|
|
||||||
static final String pathFoto = "/foto";
|
static final String pathFoto = "/fotodokumentation";
|
||||||
static final String pathQuestionnaire = "/fragenbogen";
|
static final String pathQuestionnaire = "/fragebogen-auswertung";
|
||||||
|
|
||||||
static final String pathFotoHome = "$pathFoto/home";
|
static final String pathFotoHome = "$pathFoto/home";
|
||||||
static final String pathFotoCustomer = "$pathFoto/customer";
|
static final String pathFotoCustomer = "$pathFoto/customer";
|
||||||
|
|||||||
@@ -122,3 +122,9 @@ flutter:
|
|||||||
#
|
#
|
||||||
# For details regarding fonts from package dependencies,
|
# For details regarding fonts from package dependencies,
|
||||||
# see https://flutter.dev/custom-fonts/#from-packages
|
# see https://flutter.dev/custom-fonts/#from-packages
|
||||||
|
fonts:
|
||||||
|
- family: MyFlutterApp
|
||||||
|
fonts:
|
||||||
|
- asset: assets/fonts/MyFlutterApp.ttf
|
||||||
|
style: normal
|
||||||
|
|
||||||
Reference in New Issue
Block a user