ソースを参照

Add "Wachtwoord vergeten" flow: new API call, page, and login link

Adds requestNewPassword API call (POST /user/request_new_password.json)
wired to a new WachtwoordVergeten page with a name/e-mail field and
submit action, plus a "Wachtwoord vergeten?" link on the login page
that navigates to it. Success and failure paths both show a snackbar,
the failure message sourced from the Drupal error response.
bob 1 ヶ月 前
コミット
76a58915d4

+ 103 - 0
lib/backend/api_requests/api_calls.dart

@@ -144,6 +144,78 @@ class ZZUserEstablishmentsTESTCall {
           .toList();
 }
 
+class FavorietenAgendaCall {
+  static Future<ApiCallResponse> call({
+    String? sessionname = '',
+    String? sessionid = '',
+    String? token = '',
+  }) async {
+    return ApiManager.instance.makeApiCall(
+      callName: 'FavorietenAgenda',
+      apiUrl:
+          'https://uitgaanskrant.com/nl/flutterdrup/views/flutterfavorietenagenda.json?display_id=services_1',
+      callType: ApiCallType.GET,
+      headers: {
+        'Cookie': '${sessionname}=${sessionid}',
+      },
+      params: {},
+      returnBody: true,
+      encodeBodyUtf8: false,
+      decodeUtf8: false,
+      cache: false,
+      isStreamingApi: false,
+      alwaysAllowBody: false,
+      client: ApiManager.getClient(withCredentials: true),
+    );
+  }
+
+  static String? sessionid(dynamic response) => castToType<String>(getJsonField(
+        response,
+        r'''$.sessid''',
+      ));
+  static String? sessionname(dynamic response) =>
+      castToType<String>(getJsonField(
+        response,
+        r'''$.session_name''',
+      ));
+  static String? token(dynamic response) => castToType<String>(getJsonField(
+        response,
+        r'''$.token''',
+      ));
+  static String? drupaluserid(dynamic response) =>
+      castToType<String>(getJsonField(
+        response,
+        r'''$.user.uid''',
+      ));
+  static List<String>? establishmentTitle(dynamic response) => (getJsonField(
+        response,
+        r'''$[:].node_title''',
+        true,
+      ) as List?)
+          ?.withoutNulls
+          .map((x) => castToType<String>(x))
+          .withoutNulls
+          .toList();
+  static List<String>? establishmentLogo(dynamic response) => (getJsonField(
+        response,
+        r'''$[:].logohoreca''',
+        true,
+      ) as List?)
+          ?.withoutNulls
+          .map((x) => castToType<String>(x))
+          .withoutNulls
+          .toList();
+  static List<String>? establishmentNid(dynamic response) => (getJsonField(
+        response,
+        r'''$[:].nid''',
+        true,
+      ) as List?)
+          ?.withoutNulls
+          .map((x) => castToType<String>(x))
+          .withoutNulls
+          .toList();
+}
+
 class GetcsrfCall {
   static Future<ApiCallResponse> call() async {
     return ApiManager.instance.makeApiCall(
@@ -1413,6 +1485,37 @@ class QueryCityIdCall {
       ));
 }
 
+class RequestNewPasswordCall {
+  static Future<ApiCallResponse> call({
+    String? name = '',
+  }) async {
+    final ffApiRequestBody = '''
+{"name": "[name]"}''';
+    return ApiManager.instance.makeApiCall(
+      callName: 'requestNewPassword',
+      apiUrl:
+          'https://uitgaanskrant.com/en/flutterdrup/user/request_new_password.json',
+      callType: ApiCallType.POST,
+      headers: {},
+      params: {},
+      body: ffApiRequestBody,
+      bodyType: BodyType.JSON,
+      returnBody: true,
+      encodeBodyUtf8: false,
+      decodeUtf8: false,
+      cache: false,
+      isStreamingApi: false,
+      alwaysAllowBody: false,
+    );
+  }
+
+  static String? errorMessage(dynamic response) =>
+      castToType<String>(getJsonField(
+        response,
+        r'''$[0]''',
+      ));
+}
+
 class ApiPagingParams {
   int nextPageNumber = 0;
   int numItems = 0;

+ 28 - 0
lib/flutter_flow/internationalization.dart

@@ -158,6 +158,10 @@ final kTranslationsMap = <Map<String, Map<String, String>>>[
       'nl': 'Inloggen',
       'en': 'horecagelegenheden',
     },
+    'utppw2si': {
+      'nl': 'Wachtwoord vergeten?',
+      'en': '',
+    },
     'la56ozbq': {
       'nl': 'HorecagelegenhedenOverzichtPage',
       'en': 'HorecagelegenhedenOverzichtPage',
@@ -374,6 +378,30 @@ final kTranslationsMap = <Map<String, Map<String, String>>>[
       'en': '',
     },
   },
+  // wachtwoordVergeten
+  {
+    'vrbz9a3l': {
+      'nl': 'Wachtwoord vergeten',
+      'en': '',
+    },
+    '503u6v73': {
+      'nl':
+          'Vul je gebruikersnaam of e-mailadres in. Je ontvangt een e-mail om je wachtwoord opnieuw in te stellen.',
+      'en': '',
+    },
+    'ush5uxz1': {
+      'nl': 'Gebruikersnaam of e-mailadres',
+      'en': '',
+    },
+    '0gfuq6i7': {
+      'nl': 'Verstuur',
+      'en': '',
+    },
+    's7n2u93z': {
+      'nl': 'Home',
+      'en': '',
+    },
+  },
   // PUitgaantabelKaartComponent
   {
     '4lehigdg': {

+ 8 - 1
lib/flutter_flow/nav/nav.dart

@@ -46,7 +46,9 @@ const debugRouteLinkMap = {
   '/horecagelegenhedenOverzichtPageDataType':
       'https://app.flutterflow.io/project/uitgaanskrant-1qhvtd?tab=uiBuilder&page=HorecagelegenhedenOverzichtPageDataType',
   '/horecagelegenhedenOverzichtSortPage':
-      'https://app.flutterflow.io/project/uitgaanskrant-1qhvtd?tab=uiBuilder&page=HorecagelegenhedenOverzichtSortPage'
+      'https://app.flutterflow.io/project/uitgaanskrant-1qhvtd?tab=uiBuilder&page=HorecagelegenhedenOverzichtSortPage',
+  '/wachtwoordVergeten':
+      'https://app.flutterflow.io/project/uitgaanskrant-1qhvtd?tab=uiBuilder&page=wachtwoordVergeten'
 };
 
 class AppStateNotifier extends ChangeNotifier {
@@ -212,6 +214,11 @@ GoRouter createRouter(AppStateNotifier appStateNotifier) => GoRouter(
               ParamType.String,
             ),
           ),
+        ),
+        FFRoute(
+          name: WachtwoordVergetenWidget.routeName,
+          path: WachtwoordVergetenWidget.routePath,
+          builder: (context, params) => WachtwoordVergetenWidget(),
         )
       ].map((r) => r.toRoute(appStateNotifier)).toList(),
       observers: [routeObserver],

+ 2 - 0
lib/index.dart

@@ -16,3 +16,5 @@ export '/horecagelegenhedenoverzicht/horecagelegenheden_overzicht_page_data_type
     show HorecagelegenhedenOverzichtPageDataTypeWidget;
 export '/horecagelegenhedenoverzicht/horecagelegenheden_overzicht_sort_page/horecagelegenheden_overzicht_sort_page_widget.dart'
     show HorecagelegenhedenOverzichtSortPageWidget;
+export '/wachtwoord_vergeten/wachtwoord_vergeten_widget.dart'
+    show WachtwoordVergetenWidget;

+ 34 - 0
lib/login/login/login_widget.dart

@@ -514,6 +514,40 @@ class _LoginWidgetState extends State<LoginWidget> with RouteAware {
                               borderRadius: BorderRadius.circular(8.0),
                             ),
                           ),
+                          InkWell(
+                            splashColor: Colors.transparent,
+                            focusColor: Colors.transparent,
+                            hoverColor: Colors.transparent,
+                            highlightColor: Colors.transparent,
+                            onTap: () async {
+                              context.pushNamed(
+                                  WachtwoordVergetenWidget.routeName);
+                            },
+                            child: Text(
+                              FFLocalizations.of(context).getText(
+                                'utppw2si' /* Wachtwoord vergeten? */,
+                              ),
+                              style: FlutterFlowTheme.of(context)
+                                  .bodyMedium
+                                  .override(
+                                    font: GoogleFonts.inter(
+                                      fontWeight: FlutterFlowTheme.of(context)
+                                          .bodyMedium
+                                          .fontWeight,
+                                      fontStyle: FlutterFlowTheme.of(context)
+                                          .bodyMedium
+                                          .fontStyle,
+                                    ),
+                                    letterSpacing: 0.0,
+                                    fontWeight: FlutterFlowTheme.of(context)
+                                        .bodyMedium
+                                        .fontWeight,
+                                    fontStyle: FlutterFlowTheme.of(context)
+                                        .bodyMedium
+                                        .fontStyle,
+                                  ),
+                            ),
+                          ),
                           FFButtonWidget(
                             onPressed: () async {
                               context.pushNamed(

+ 82 - 0
lib/wachtwoord_vergeten/wachtwoord_vergeten_model.dart

@@ -0,0 +1,82 @@
+import '/backend/api_requests/api_calls.dart';
+import '/flutter_flow/flutter_flow_theme.dart';
+import '/flutter_flow/flutter_flow_util.dart';
+import '/flutter_flow/flutter_flow_widgets.dart';
+import 'dart:ui';
+import 'wachtwoord_vergeten_widget.dart' show WachtwoordVergetenWidget;
+import 'package:flutter/material.dart';
+import 'package:flutter_spinkit/flutter_spinkit.dart';
+import 'package:google_fonts/google_fonts.dart';
+import 'package:provider/provider.dart';
+
+class WachtwoordVergetenModel
+    extends FlutterFlowModel<WachtwoordVergetenWidget> {
+  ///  State fields for stateful widgets in this page.
+
+  // State field(s) for NaamOfEmailField widget.
+  FocusNode? naamOfEmailFieldFocusNode;
+  TextEditingController? naamOfEmailFieldTextController;
+  String? Function(BuildContext, String?)?
+      naamOfEmailFieldTextControllerValidator;
+  // Stores action output result for [Backend Call - API (requestNewPassword)] action in Button widget.
+  ApiCallResponse? _apiResultuoz;
+  set apiResultuoz(ApiCallResponse? value) {
+    _apiResultuoz = value;
+    debugLogWidgetClass(this);
+  }
+
+  ApiCallResponse? get apiResultuoz => _apiResultuoz;
+
+  final Map<String, DebugDataField> debugGeneratorVariables = {};
+  final Map<String, DebugDataField> debugBackendQueries = {};
+  final Map<String, FlutterFlowModel> widgetBuilderComponents = {};
+  @override
+  void initState(BuildContext context) {
+    debugLogWidgetClass(this);
+  }
+
+  @override
+  void dispose() {
+    naamOfEmailFieldFocusNode?.dispose();
+    naamOfEmailFieldTextController?.dispose();
+  }
+
+  @override
+  WidgetClassDebugData toWidgetClassDebugData() => WidgetClassDebugData(
+        widgetStates: {
+          'naamOfEmailFieldText': debugSerializeParam(
+            naamOfEmailFieldTextController?.text,
+            ParamType.String,
+            link:
+                'https://app.flutterflow.io/project/uitgaanskrant-1qhvtd?tab=uiBuilder&page=wachtwoordVergeten',
+            name: 'String',
+            nullable: true,
+          )
+        }.entries,
+        actionOutputs: {
+          'apiResultuoz': debugSerializeParam(
+            apiResultuoz,
+            ParamType.ApiResponse,
+            link:
+                'https://app.flutterflow.io/project/uitgaanskrant-1qhvtd?tab=uiBuilder&page=wachtwoordVergeten',
+            name: 'ApiCallResponse',
+            nullable: true,
+          )
+        }.entries,
+        generatorVariables: debugGeneratorVariables.entries,
+        backendQueries: debugBackendQueries.entries,
+        componentStates: {
+          ...widgetBuilderComponents.map(
+            (key, value) => MapEntry(
+              key,
+              value.toWidgetClassDebugData(),
+            ),
+          ),
+        }.withoutNulls.entries,
+        link:
+            'https://app.flutterflow.io/project/uitgaanskrant-1qhvtd/tab=uiBuilder&page=wachtwoordVergeten',
+        searchReference:
+            'reference=OhJ3YWNodHdvb3JkVmVyZ2V0ZW5QAVoSd2FjaHR3b29yZFZlcmdldGVu',
+        widgetClassName: 'wachtwoordVergeten',
+      );
+}

+ 320 - 0
lib/wachtwoord_vergeten/wachtwoord_vergeten_widget.dart

@@ -0,0 +1,320 @@
+import '/backend/api_requests/api_calls.dart';
+import '/flutter_flow/flutter_flow_theme.dart';
+import '/flutter_flow/flutter_flow_util.dart';
+import '/flutter_flow/flutter_flow_widgets.dart';
+import 'dart:ui';
+import 'package:flutter/material.dart';
+import 'package:flutter_spinkit/flutter_spinkit.dart';
+import 'package:google_fonts/google_fonts.dart';
+import 'package:provider/provider.dart';
+import 'wachtwoord_vergeten_model.dart';
+export 'wachtwoord_vergeten_model.dart';
+
+class WachtwoordVergetenWidget extends StatefulWidget {
+  const WachtwoordVergetenWidget({super.key});
+
+  static String routeName = 'wachtwoordVergeten';
+  static String routePath = '/wachtwoordVergeten';
+
+  @override
+  State<WachtwoordVergetenWidget> createState() =>
+      _WachtwoordVergetenWidgetState();
+}
+
+class _WachtwoordVergetenWidgetState extends State<WachtwoordVergetenWidget>
+    with RouteAware {
+  late WachtwoordVergetenModel _model;
+
+  final scaffoldKey = GlobalKey<ScaffoldState>();
+
+  @override
+  void initState() {
+    super.initState();
+    _model = createModel(context, () => WachtwoordVergetenModel());
+
+    _model.naamOfEmailFieldTextController ??= TextEditingController()
+      ..addListener(() {
+        debugLogWidgetClass(_model);
+      });
+    _model.naamOfEmailFieldFocusNode ??= FocusNode();
+  }
+
+  @override
+  void dispose() {
+    routeObserver.unsubscribe(this);
+
+    _model.dispose();
+
+    super.dispose();
+  }
+
+  @override
+  void didUpdateWidget(WachtwoordVergetenWidget oldWidget) {
+    super.didUpdateWidget(oldWidget);
+    _model.widget = widget;
+  }
+
+  @override
+  void didChangeDependencies() {
+    super.didChangeDependencies();
+    final route = DebugModalRoute.of(context);
+    if (route != null) {
+      routeObserver.subscribe(this, route);
+    }
+    debugLogGlobalProperty(context);
+  }
+
+  @override
+  void didPopNext() {
+    if (mounted && DebugFlutterFlowModelContext.maybeOf(context) == null) {
+      setState(() => _model.isRouteVisible = true);
+      debugLogWidgetClass(_model);
+    }
+  }
+
+  @override
+  void didPush() {
+    if (mounted && DebugFlutterFlowModelContext.maybeOf(context) == null) {
+      setState(() => _model.isRouteVisible = true);
+      debugLogWidgetClass(_model);
+    }
+  }
+
+  @override
+  void didPop() {
+    _model.isRouteVisible = false;
+  }
+
+  @override
+  void didPushNext() {
+    _model.isRouteVisible = false;
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    DebugFlutterFlowModelContext.maybeOf(context)
+        ?.parentModelCallback
+        ?.call(_model);
+
+    return GestureDetector(
+      onTap: () {
+        FocusScope.of(context).unfocus();
+        FocusManager.instance.primaryFocus?.unfocus();
+      },
+      child: Scaffold(
+        key: scaffoldKey,
+        backgroundColor: FlutterFlowTheme.of(context).primaryBackground,
+        body: SafeArea(
+          top: true,
+          child: Column(
+            mainAxisSize: MainAxisSize.max,
+            children: [
+              Text(
+                FFLocalizations.of(context).getText(
+                  'vrbz9a3l' /* Wachtwoord vergeten */,
+                ),
+                style: FlutterFlowTheme.of(context).bodyMedium.override(
+                      font: GoogleFonts.inter(
+                        fontWeight:
+                            FlutterFlowTheme.of(context).bodyMedium.fontWeight,
+                        fontStyle:
+                            FlutterFlowTheme.of(context).bodyMedium.fontStyle,
+                      ),
+                      letterSpacing: 0.0,
+                      fontWeight:
+                          FlutterFlowTheme.of(context).bodyMedium.fontWeight,
+                      fontStyle:
+                          FlutterFlowTheme.of(context).bodyMedium.fontStyle,
+                    ),
+              ),
+              Text(
+                FFLocalizations.of(context).getText(
+                  '503u6v73' /* Vul je gebruikersnaam of e-mai... */,
+                ),
+                style: FlutterFlowTheme.of(context).bodyMedium.override(
+                      font: GoogleFonts.inter(
+                        fontWeight:
+                            FlutterFlowTheme.of(context).bodyMedium.fontWeight,
+                        fontStyle:
+                            FlutterFlowTheme.of(context).bodyMedium.fontStyle,
+                      ),
+                      letterSpacing: 0.0,
+                      fontWeight:
+                          FlutterFlowTheme.of(context).bodyMedium.fontWeight,
+                      fontStyle:
+                          FlutterFlowTheme.of(context).bodyMedium.fontStyle,
+                    ),
+              ),
+              Container(
+                width: 200.0,
+                child: TextFormField(
+                  controller: _model.naamOfEmailFieldTextController,
+                  focusNode: _model.naamOfEmailFieldFocusNode,
+                  autofocus: false,
+                  enabled: true,
+                  obscureText: false,
+                  decoration: InputDecoration(
+                    isDense: true,
+                    labelStyle:
+                        FlutterFlowTheme.of(context).labelMedium.override(
+                              font: GoogleFonts.inter(
+                                fontWeight: FlutterFlowTheme.of(context)
+                                    .labelMedium
+                                    .fontWeight,
+                                fontStyle: FlutterFlowTheme.of(context)
+                                    .labelMedium
+                                    .fontStyle,
+                              ),
+                              letterSpacing: 0.0,
+                              fontWeight: FlutterFlowTheme.of(context)
+                                  .labelMedium
+                                  .fontWeight,
+                              fontStyle: FlutterFlowTheme.of(context)
+                                  .labelMedium
+                                  .fontStyle,
+                            ),
+                    hintText: FFLocalizations.of(context).getText(
+                      'ush5uxz1' /* Gebruikersnaam of e-mailadres */,
+                    ),
+                    hintStyle:
+                        FlutterFlowTheme.of(context).labelMedium.override(
+                              font: GoogleFonts.inter(
+                                fontWeight: FlutterFlowTheme.of(context)
+                                    .labelMedium
+                                    .fontWeight,
+                                fontStyle: FlutterFlowTheme.of(context)
+                                    .labelMedium
+                                    .fontStyle,
+                              ),
+                              letterSpacing: 0.0,
+                              fontWeight: FlutterFlowTheme.of(context)
+                                  .labelMedium
+                                  .fontWeight,
+                              fontStyle: FlutterFlowTheme.of(context)
+                                  .labelMedium
+                                  .fontStyle,
+                            ),
+                    enabledBorder: OutlineInputBorder(
+                      borderSide: BorderSide(
+                        color: Color(0x00000000),
+                        width: 1.0,
+                      ),
+                      borderRadius: BorderRadius.circular(8.0),
+                    ),
+                    focusedBorder: OutlineInputBorder(
+                      borderSide: BorderSide(
+                        color: Color(0x00000000),
+                        width: 1.0,
+                      ),
+                      borderRadius: BorderRadius.circular(8.0),
+                    ),
+                    errorBorder: OutlineInputBorder(
+                      borderSide: BorderSide(
+                        color: FlutterFlowTheme.of(context).error,
+                        width: 1.0,
+                      ),
+                      borderRadius: BorderRadius.circular(8.0),
+                    ),
+                    focusedErrorBorder: OutlineInputBorder(
+                      borderSide: BorderSide(
+                        color: FlutterFlowTheme.of(context).error,
+                        width: 1.0,
+                      ),
+                      borderRadius: BorderRadius.circular(8.0),
+                    ),
+                    filled: true,
+                    fillColor: FlutterFlowTheme.of(context).secondaryBackground,
+                  ),
+                  style: FlutterFlowTheme.of(context).bodyMedium.override(
+                        font: GoogleFonts.inter(
+                          fontWeight: FlutterFlowTheme.of(context)
+                              .bodyMedium
+                              .fontWeight,
+                          fontStyle:
+                              FlutterFlowTheme.of(context).bodyMedium.fontStyle,
+                        ),
+                        letterSpacing: 0.0,
+                        fontWeight:
+                            FlutterFlowTheme.of(context).bodyMedium.fontWeight,
+                        fontStyle:
+                            FlutterFlowTheme.of(context).bodyMedium.fontStyle,
+                      ),
+                  cursorColor: FlutterFlowTheme.of(context).primaryText,
+                  enableInteractiveSelection: true,
+                  validator: _model.naamOfEmailFieldTextControllerValidator
+                      .asValidator(context),
+                ),
+              ),
+              FFButtonWidget(
+                onPressed: () async {
+                  _model.apiResultuoz = await RequestNewPasswordCall.call(
+                    name: _model.naamOfEmailFieldTextController.text,
+                  );
+
+                  if ((_model.apiResultuoz?.succeeded ?? true)) {
+                    ScaffoldMessenger.of(context).showSnackBar(
+                      SnackBar(
+                        content: Text(
+                          'Als dit account bestaat, ontvang je een e-mail om je wachtwoord opnieuw in te stellen.',
+                          style: TextStyle(
+                            color: FlutterFlowTheme.of(context).primaryText,
+                          ),
+                        ),
+                        duration: Duration(milliseconds: 4000),
+                        backgroundColor: FlutterFlowTheme.of(context).secondary,
+                      ),
+                    );
+                  } else {
+                    ScaffoldMessenger.of(context).showSnackBar(
+                      SnackBar(
+                        content: Text(
+                          RequestNewPasswordCall.errorMessage(
+                            (_model.apiResultuoz?.jsonBody ?? ''),
+                          )!,
+                          style: TextStyle(
+                            color: FlutterFlowTheme.of(context).primaryText,
+                          ),
+                        ),
+                        duration: Duration(milliseconds: 4000),
+                        backgroundColor: FlutterFlowTheme.of(context).secondary,
+                      ),
+                    );
+                  }
+
+                  safeSetState(() {});
+                },
+                text: FFLocalizations.of(context).getText(
+                  '0gfuq6i7' /* Verstuur */,
+                ),
+                options: FFButtonOptions(
+                  height: 40.0,
+                  padding: EdgeInsetsDirectional.fromSTEB(16.0, 0.0, 16.0, 0.0),
+                  iconPadding:
+                      EdgeInsetsDirectional.fromSTEB(0.0, 0.0, 0.0, 0.0),
+                  color: FlutterFlowTheme.of(context).primary,
+                  textStyle: FlutterFlowTheme.of(context).titleSmall.override(
+                        font: GoogleFonts.interTight(
+                          fontWeight: FlutterFlowTheme.of(context)
+                              .titleSmall
+                              .fontWeight,
+                          fontStyle:
+                              FlutterFlowTheme.of(context).titleSmall.fontStyle,
+                        ),
+                        color: Colors.white,
+                        letterSpacing: 0.0,
+                        fontWeight:
+                            FlutterFlowTheme.of(context).titleSmall.fontWeight,
+                        fontStyle:
+                            FlutterFlowTheme.of(context).titleSmall.fontStyle,
+                      ),
+                  elevation: 0.0,
+                  borderRadius: BorderRadius.circular(8.0),
+                ),
+              ),
+            ],
+          ),
+        ),
+      ),
+    );
+  }
+}