diff --git a/lib/consts.dart b/lib/consts.dart index 71b88ee7..1d7aa6e4 100644 --- a/lib/consts.dart +++ b/lib/consts.dart @@ -510,3 +510,4 @@ const kMsgClearHistory = const kMsgClearHistorySuccess = 'History cleared successfully'; const kMsgClearHistoryError = 'Error clearing history'; const kMsgShareError = "Unable to share"; +const kLabelGenerateUI = "Generate UI"; diff --git a/lib/widgets/ai_ui_desginer_widgets.dart b/lib/widgets/ai_ui_desginer_widgets.dart new file mode 100644 index 00000000..b20f85ad --- /dev/null +++ b/lib/widgets/ai_ui_desginer_widgets.dart @@ -0,0 +1,305 @@ +import 'package:apidash/consts.dart'; +import 'package:apidash/widgets/widget_sending.dart'; +import 'package:apidash_design_system/apidash_design_system.dart'; +import 'package:flutter/material.dart'; + +void showCustomDialog(BuildContext context, String content) { + showDialog( + context: context, + builder: (BuildContext context) { + return Dialog( + backgroundColor: Color.fromARGB(255, 16, 20, 24), // Dark background + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12.0), + ), + child: DialogContents( + content: content, + ), + ); + }, + ); +} + +class DialogContents extends StatefulWidget { + final String content; + const DialogContents({ + super.key, + required this.content, + }); + + @override + State createState() => _DialogContentsState(); +} + +class _DialogContentsState extends State { + int index = 0; + @override + Widget build(BuildContext context) { + return IndexedStack( + index: index, + children: [ + FrameWorkSelectorPage( + content: widget.content, + onNext: () { + setState(() { + index = 1; + }); + }, + ), + SizedBox( + width: MediaQuery.of(context).size.width * 0.6, + child: Center( + child: Padding( + padding: const EdgeInsets.only(top: 40.0), + child: GestureDetector( + onTap: () { + setState(() { + index = 2; + }); + }, + child: Container( + height: 500, + child: SendingWidget( + startSendingTime: DateTime.now(), + ), + ), + ), + ), + ), + ), + EditorPage( + onNext: () {}, + ) + ], + ); + } +} + +class FrameWorkSelectorPage extends StatefulWidget { + final String content; + final Function() onNext; + const FrameWorkSelectorPage( + {super.key, required this.content, required this.onNext}); + + @override + State createState() => _FrameWorkSelectorPageState(); +} + +class _FrameWorkSelectorPageState extends State { + String? selectedFramework; + TextEditingController controller = TextEditingController(); + + @override + void initState() { + controller.text = widget.content; + super.initState(); + } + + @override + Widget build(BuildContext context) { + final textContainerdecoration = BoxDecoration( + color: Color.alphaBlend( + (Theme.of(context).brightness == Brightness.dark + ? Theme.of(context).colorScheme.onPrimaryContainer + : Theme.of(context).colorScheme.primaryContainer) + .withOpacity(kForegroundOpacity), + Theme.of(context).colorScheme.surface), + border: Border.all( + color: Theme.of(context).colorScheme.surfaceContainerHighest), + borderRadius: kBorderRadius8, + ); + + return Container( + width: MediaQuery.of(context).size.width * 0.6, // Large dialog + padding: EdgeInsets.all(20), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Container( + width: double.maxFinite, + padding: kP8, + decoration: textContainerdecoration, + child: SingleChildScrollView( + child: TextField( + controller: controller, + maxLines: null, + style: kCodeStyle, + ), + ), + ), + ), + kVSpacer20, + // Text( + // "Select Framework", + // style: TextStyle( + // color: Colors.white, + // fontSize: 18, + // fontWeight: FontWeight.bold, + // ), + // ), + // SizedBox(height: 10), + // DropdownButtonFormField( + // dropdownColor: Color(0xFF2D2D2D), + // decoration: InputDecoration( + // filled: true, + // fillColor: Color(0xFF2D2D2D), + // border: OutlineInputBorder( + // borderRadius: BorderRadius.circular(8.0), + // ), + // ), + // value: selectedFramework, + // items: ["Flutter", "ReactJS"].map((String value) { + // return DropdownMenuItem( + // value: value, + // child: Text( + // value, + // style: TextStyle(color: Colors.white), + // ), + // ); + // }).toList(), + // onChanged: (newValue) { + // selectedFramework = newValue; + // setState(() {}); + // }, + // ), + // kVSpacer20, + Align( + alignment: Alignment.centerRight, + child: FilledButton.tonalIcon( + style: FilledButton.styleFrom( + padding: kPh12, + minimumSize: const Size(44, 44), + ), + onPressed: () { + widget.onNext(); + }, + icon: Icon( + Icons.generating_tokens, + ), + label: const SizedBox( + child: Text( + kLabelGenerateUI, + ), + ), + ), + ), + ], + ), + ); + } +} + +class EditorPage extends StatefulWidget { + final Function() onNext; + const EditorPage({super.key, required this.onNext}); + + @override + State createState() => _EditorPageState(); +} + +class _EditorPageState extends State { + @override + Widget build(BuildContext context) { + return Container( + width: MediaQuery.of(context).size.width * 0.6, // Large dialog + padding: EdgeInsets.all(20), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Generated Component", + style: TextStyle( + fontSize: 20, + ), + ), + kVSpacer20, + Expanded( + child: Center( + child: Container( + padding: EdgeInsets.all(20), + decoration: BoxDecoration( + border: Border.all(color: Colors.white), + ), + child: Container( + color: Colors.grey, + ), + ), + ), + ), + kVSpacer20, + Container( + width: double.infinity, + decoration: BoxDecoration( + color: Colors.black, // Dark background + borderRadius: BorderRadius.circular(15), + border: Border.all( + color: Colors.grey, // Border color + width: 1, + ), + ), + child: TextField( + maxLines: 3, // Makes the text box taller + style: TextStyle(color: Colors.white), // White text + decoration: InputDecoration( + hintText: 'Any Modifications?', + hintStyle: TextStyle(color: Colors.grey), // Grey hint text + border: InputBorder.none, // Removes the default border + contentPadding: EdgeInsets.all(16), // Padding inside the box + ), + ), + ), + kVSpacer20, + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Align( + alignment: Alignment.centerRight, + child: FilledButton.tonalIcon( + style: FilledButton.styleFrom( + padding: kPh12, + minimumSize: const Size(44, 44), + ), + onPressed: () { + widget.onNext(); + }, + icon: Icon( + Icons.download, + ), + label: const SizedBox( + child: Text( + "Export Code", + ), + ), + ), + ), + kHSpacer10, + Align( + alignment: Alignment.centerRight, + child: FilledButton.tonalIcon( + style: FilledButton.styleFrom( + padding: kPh12, + minimumSize: const Size(44, 44), + ), + onPressed: () { + widget.onNext(); + }, + icon: Icon( + Icons.generating_tokens, + ), + label: const SizedBox( + child: Text( + "Make Modifications", + ), + ), + ), + ), + ], + ), + ], + ), + ); + } +} diff --git a/lib/widgets/response_pane_header.dart b/lib/widgets/response_pane_header.dart index 406889d8..6417a1d9 100644 --- a/lib/widgets/response_pane_header.dart +++ b/lib/widgets/response_pane_header.dart @@ -1,10 +1,13 @@ +import 'package:apidash/providers/collection_providers.dart'; +import 'package:apidash/widgets/ai_ui_desginer_widgets.dart'; import 'package:flutter/material.dart'; import 'package:apidash_design_system/apidash_design_system.dart'; import 'package:apidash/utils/utils.dart'; import 'package:apidash/consts.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'button_clear_response.dart'; -class ResponsePaneHeader extends StatelessWidget { +class ResponsePaneHeader extends ConsumerWidget { const ResponsePaneHeader({ super.key, this.responseStatus, @@ -19,7 +22,7 @@ class ResponsePaneHeader extends StatelessWidget { final VoidCallback? onClearResponse; @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { final bool showClearButton = onClearResponse != null; return Padding( padding: kPv8, @@ -43,6 +46,25 @@ class ResponsePaneHeader extends StatelessWidget { ), ), ), + FilledButton.tonalIcon( + style: FilledButton.styleFrom( + padding: kPh12, + minimumSize: const Size(44, 44), + ), + onPressed: () { + final model = ref.watch(selectedRequestModelProvider + .select((value) => value?.httpResponseModel)); + showCustomDialog(context, model?.formattedBody ?? ""); + }, + icon: Icon( + Icons.generating_tokens, + ), + label: const SizedBox( + child: Text( + kLabelGenerateUI, + ), + ), + ), kHSpacer10, Text( humanizeDuration(time),