Commit 19cf9edf authored by Erfan Jazeb Nikoo's avatar Erfan Jazeb Nikoo
Browse files

Merge branch 'erfan/ui-3' into 'dev'

erfan/ui-3

See merge request !450
parents f5c54886 408b486f
Pipeline #11284 passed with stages
in 7 minutes and 57 seconds
Showing with 578 additions and 135 deletions
+578 -135
......@@ -15,9 +15,10 @@ import 'package:memri/screens/workspace/data/importers/importer_error_screen.dar
import 'package:memri/screens/workspace/data_screen.dart';
import 'package:memri/screens/workspace/oauth_screen.dart';
import 'package:memri/screens/workspace/pod_keys_screen.dart';
import 'package:memri/screens/workspace/projects/projects_preview_template.dart';
import 'package:memri/screens/workspace/projects/projects_select_template_screen.dart';
import 'package:memri/screens/workspace/projects/projects_train_model_screen.dart';
import 'package:memri/screens/workspace/projects/projects_app_preview_screen.dart';
import 'package:memri/screens/workspace/projects/projects_preview_app_screen.dart';
import 'package:memri/screens/workspace/projects/projects_create_screen.dart';
import 'package:memri/screens/workspace/projects/projects_label_data_screen.dart';
import 'package:memri/screens/workspace/projects_screen.dart';
......@@ -108,8 +109,12 @@ var projectsSelectTemplateScreenHandler = Handler(
handlerFunc: (_, Map<String, List<String>> params) =>
ProjectsSelectTemplateScreen(id: params['id']?.first ?? ''));
var projectsAppPreviewScreenHandler = Handler(
var projectsPreviewTemplateScreenHandler = Handler(
handlerFunc: (_, Map<String, List<String>> params) =>
ProjectsAppPreviewScreen(id: params['id']?.first ?? ''));
ProjectsPreviewTemplate(template: params['template']?.first ?? ''));
var projectsPreviewAppScreenHandler = Handler(
handlerFunc: (_, Map<String, List<String>> params) =>
ProjectsPreviewAppScreen(id: params['id']?.first ?? ''));
var cvuHandler = Handler(handlerFunc: (_, __) => CVUScreen());
......@@ -29,7 +29,9 @@ class Routes {
static String projectsLabelData = '/workspace/projects/label_data';
static String projectsTrainModel = '/workspace/projects/train_model';
static String projectsSelectTemplate = '/workspace/projects/select_template';
static String projectsAppPreview = '/workspace/projects/app_preview';
static String projectsPreviewTemplate =
'/workspace/projects/select_template/preview';
static String projectsPreviewApp = '/workspace/projects/preview_app';
/// Apps
static String apps = '/workspace/apps';
......@@ -64,7 +66,9 @@ class Routes {
router.define(projectsTrainModel, handler: projectsTrainModelScreenHandler);
router.define(projectsSelectTemplate,
handler: projectsSelectTemplateScreenHandler);
router.define(projectsAppPreview, handler: projectsAppPreviewScreenHandler);
router.define(Routes.projectsPreviewTemplate,
handler: projectsPreviewTemplateScreenHandler);
router.define(projectsPreviewApp, handler: projectsPreviewAppScreenHandler);
router.define(cvu, handler: cvuHandler);
}
......
......@@ -316,6 +316,34 @@ query {
return result;
}
Future<List<Item>> fetchDataApp(String id) async =>
await getResultAsItemList(query: '''
query {
Plugin(filter: {id: {eq: "$id"}}) {
id
containerImage
configJson
config
dataType
pluginDescription
name
pluginModule
pluginName
pluginType
gitProjectId
templateSettings {
templateName
templateId
dataSource
labelOption {
name
}
}
}
}
''');
Future<List<Item>> fetchDataApps() async =>
await getResultAsItemList(query: '''
query {
......
......@@ -2,9 +2,10 @@ import 'package:memri/constants/app_logger.dart';
import 'package:url_launcher/url_launcher_string.dart';
class NetworkService {
Future<void> openLink(String url) async {
Future<void> openLink(String url, {bool isNewTab = false}) async {
await canLaunchUrlString(url)
? await launchUrlString(url)
? await launchUrlString(url,
webOnlyWindowName: isNewTab ? '_blank' : '_self')
: AppLogger.err('Could not launch $url');
}
}
......@@ -52,10 +52,10 @@ Future<void> setup() async {
));
locator.registerLazySingleton<ImporterProvider>(() =>
ImporterProvider(locator(), locator(), locator(), locator(), locator()));
locator.registerLazySingleton<ProjectProvider>(
() => ProjectProvider(locator(), locator(), locator(), locator()));
locator.registerLazySingleton<DataAppProvider>(
() => DataAppProvider(locator(), locator(), locator()));
locator.registerLazySingleton<ProjectProvider>(() =>
ProjectProvider(locator(), locator(), locator(), locator(), locator()));
locator.registerLazySingleton<DataAppProvider>(() =>
DataAppProvider(locator(), locator(), locator(), locator(), locator()));
locator.registerLazySingleton<InboxProvider>(
() => InboxProvider(locator(), locator(), locator()));
......
import 'dart:async';
import 'dart:convert';
import 'dart:html' as html;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
......@@ -11,8 +10,12 @@ import 'package:memri/core/models/item.dart';
import 'package:memri/core/models/running_plugin.dart';
import 'package:memri/core/services/gitlab_service.dart';
import 'package:memri/core/services/graph_service.dart';
import 'package:memri/core/services/network_service.dart';
import 'package:memri/core/services/pod_service.dart';
import 'package:memri/providers/project_provider.dart';
import 'package:memri/utilities/helpers/app_helper.dart';
import 'package:memri/widgets/templates/templates_table.dart';
import 'package:memri/widgets/templates/templates_visualization.dart';
enum AppState { deploying, initialPredictions, deployed, fetchingAppState }
......@@ -52,8 +55,11 @@ class DataAppProvider with ChangeNotifier {
final PodService _podService;
final GraphService _graphService;
final GitlabService _gitlabService;
final ProjectProvider _projectProvider;
final NetworkService _networkService;
DataAppProvider(this._podService, this._graphService, this._gitlabService);
DataAppProvider(this._podService, this._graphService, this._gitlabService,
this._projectProvider, this._networkService);
// TODO handle schema checking somewhere else
final List<String> _runProperties = [
......@@ -92,10 +98,24 @@ class DataAppProvider with ChangeNotifier {
String? currentDataType;
String? currentDataSource;
Item? _currentMemriProject;
Widget? _currentDataAppTemplate;
Item? currentPlugin;
bool triggerForCurrentPluginRunRegistered = false;
Widget get currentDataAppTemplate => _currentDataAppTemplate ?? SizedBox();
Item? get currentMemriProject => _currentMemriProject;
String? get currentMemriProjectId => _currentMemriProject?.get('id');
String? get mockItemDataSourceForGit =>
currentGitProjectId == null ? null : '_preview-$currentGitProjectId';
InterfaceTemplate? get selectedInterfaceTemplates =>
_projectProvider.selectedInterfaceTemplates[currentMemriProjectId] ??
InterfaceTemplate.table;
set currentMemriProject(Item? item) {
if (item != null) {
_currentMemriProject = item;
......@@ -104,12 +124,19 @@ class DataAppProvider with ChangeNotifier {
}
}
Item? get currentMemriProject => _currentMemriProject;
String? get currentMemriProjectId => _currentMemriProject?.get('id');
String? get mockItemDataSourceForGit =>
currentGitProjectId == null ? null : '_preview-$currentGitProjectId';
void setCurrentInterfaceTemplate(InterfaceTemplate selectedTemplate) {
switch (selectedTemplate) {
case InterfaceTemplate.table:
_currentDataAppTemplate = TemplatesTable();
break;
case InterfaceTemplate.visualization:
_currentDataAppTemplate = TemplatesVisualization();
break;
case InterfaceTemplate.inbox:
_currentDataAppTemplate = SizedBox();
break;
}
}
reset() {
currentPluginRun = null;
......@@ -173,10 +200,10 @@ class DataAppProvider with ChangeNotifier {
if (refresh) notifyListeners();
}
handlePublishApp(BuildContext context, Item project, String dataSource,
int? selectedTemplate) async {
handlePublishApp(
BuildContext context, Item project, String dataSource) async {
// TODO delete previous app with name + gitProjectId
await createAndConnectPluginItem(project, selectedTemplate);
await createAndConnectPluginItem(project);
currentPluginRunSubscription?.cancel();
RouteNavigator.navigateTo(context: context, route: Routes.apps);
}
......@@ -185,7 +212,7 @@ class DataAppProvider with ChangeNotifier {
// runIndexerPluginFromGit(project: project, changeState: false);
// }
createAndConnectPluginItem(Item project, int? selectedTemplateId) async {
createAndConnectPluginItem(Item project) async {
String projectName = project.get('name')!;
// Create data app from current gitlab id
var metadata = await getMetadata(currentGitProjectId!);
......@@ -214,25 +241,25 @@ class DataAppProvider with ChangeNotifier {
// await _podService.createItem(item: plugin);
await _podService
.bulkAction(createItems: [plugin], createEdges: [projectEdge, runEdge]);
await createAndConnectTemplateSettings(plugin, project, selectedTemplateId);
await createAndConnectTemplateSettings(plugin, project);
dataApps.add(plugin);
AppLogger.debug('Created plugin: $projectName');
notifyListeners();
}
createAndConnectTemplateSettings(
Item plugin, Item project, int? selectedTemplateId) {
createAndConnectTemplateSettings(Item plugin, Item project) {
List<Edge> createEdges = [];
List<Item> createItems = [];
// Make settings, add properties
var settings = Item(type: 'TemplateSettings');
settings.properties['dataSource'] = currentDataSource;
settings.properties['templateId'] = selectedTemplateId;
if (selectedTemplateId != null) {
settings.properties['templateName'] =
app.projectTemplates.keys.elementAt(selectedTemplateId);
settings.properties['templateId'] =
InterfaceTemplate.values.indexOf(selectedInterfaceTemplates!);
if (selectedInterfaceTemplates != null) {
settings.properties['templateName'] = selectedInterfaceTemplates!.name;
}
// Add edges
......@@ -466,8 +493,7 @@ class DataAppProvider with ChangeNotifier {
var existingChannelPhotos =
realMessageChannel.getEdgeTargets('photo') ?? [];
for (var existingChannelPhoto in existingChannelPhotos) {
createEdges
.add(mockChannel.addEdge('sender', existingChannelPhoto));
createEdges.add(mockChannel.addEdge('photo', existingChannelPhoto));
}
}
createEdges.add(mockMessage.addEdge('messageChannel', mockChannel));
......@@ -565,35 +591,59 @@ class DataAppProvider with ChangeNotifier {
List<Item> dataApps = [];
List<Item> dataAppsInboxList = [];
bool get anyDataAppExist =>
dataApps.isNotEmpty || dataAppsInboxList.isNotEmpty;
fetchDataApps() async {
_handleAppListFetching();
// make sure that no old pluginRuns are set
// make sure.currentPluginRun = null;
currentPluginRun = null;
dataApps =
List<Item> apps =
await _graphService.fetchDataApps().whenComplete(_handleAppListFetched);
bool exist = false;
for (Item dataApp in dataApps) {
for (Item dataApp in apps) {
exist = false;
for (Item inbox in dataAppsInboxList) {
if (inbox.get('pluginName') == dataApp.get('pluginName')) {
if (inbox.get('id') == dataApp.get('id')) {
exist = true;
}
}
if (hasDataAppInbox(dataApp) && !exist) {
dataAppsInboxList.add(dataApp);
} else {
exist = false;
for (Item inbox in dataApps) {
if (inbox.get('id') == dataApp.get('id')) {
exist = true;
}
}
if (!exist) dataApps.add(dataApp);
}
}
}
bool hasDataAppInbox(Item app) =>
bool hasDataAppTemplate(Item app) =>
app.getEdgeTargets('templateSettings') != null &&
app.getEdgeTargets('templateSettings')!.isNotEmpty &&
app.getEdgeTargets('templateSettings')![0].get('templateId') != null;
app.getEdgeTargets('templateSettings')!.isNotEmpty;
bool hasDataAppInbox(Item app) =>
hasDataAppTemplate(app) &&
app.getEdgeTargets('templateSettings')![0].get('templateId') != null &&
app.getEdgeTargets('templateSettings')![0].get('templateId') ==
InterfaceTemplate.values.indexOf(InterfaceTemplate.inbox);
Future<void> setupDataApp(String pluginId) async {
currentPlugin = (await getPlugin(pluginId))!;
Item? plugin;
for (Item app in dataApps) {
if (app.id == pluginId) {
plugin = app;
}
}
currentPlugin =
plugin ?? (await _graphService.fetchDataApp(pluginId)).first;
List<Item> runs = await getPluginRunsForPlugin(pluginId);
if (runs.isEmpty) {
......@@ -601,17 +651,27 @@ class DataAppProvider with ChangeNotifier {
} else {
currentPluginRun = runs[0];
}
InterfaceTemplate template = InterfaceTemplate.table;
if (currentPlugin != null && hasDataAppTemplate(currentPlugin!)) {
var templateId = currentPlugin!
.getEdgeTargets('templateSettings')![0]
.get('templateId');
template = InterfaceTemplate.values.elementAt(templateId);
}
setCurrentInterfaceTemplate(template);
}
handleOpenDataApp(BuildContext context, Item plugin, AppTab appTab) async {
var pluginId = plugin.get('id');
if (kIsWeb) {
html.window.open(
_gitlabService.baseUrl +
Routes.appConfigure +
'?id=$pluginId&tab=${appTab.toUrlString()}',
'_blank');
_networkService.openLink(
_gitlabService.baseUrl +
Routes.appConfigure +
'?id=$pluginId&tab=${appTab.toUrlString()}',
isNewTab: true,
);
} else {
RouteNavigator.navigateTo(
route: Routes.appConfigure,
......
......@@ -68,7 +68,7 @@ class NavigationProvider with ChangeNotifier {
}
void openDataExplorerLink() {
_networkService.openLink(app.settings.dataExplorerUrl);
_networkService.openLink(app.settings.dataExplorerUrl, isNewTab: true);
_analyticsService.logNavigationButton(AnalyticsEvents.dataExplorer);
}
......
// ignore_for_file: cancel_subscriptions
import 'dart:async';
import 'dart:js' as js;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:memri/configs/routes/route_navigator.dart';
......@@ -13,6 +13,7 @@ import 'package:memri/core/models/pipeline_job.dart';
import 'package:memri/core/services/analytics_service.dart';
import 'package:memri/core/services/gitlab_service.dart';
import 'package:memri/core/services/graph_service.dart';
import 'package:memri/core/services/network_service.dart';
import 'package:memri/core/services/pod_service.dart';
import 'package:memri/utilities/helpers/app_helper.dart';
import 'package:memri/widgets/dialogs/custom_alert_dialog.dart';
......@@ -23,12 +24,14 @@ class ProjectProvider with ChangeNotifier {
final GraphService _graphService;
final GitlabService _gitlabService;
final AnalyticsService _analyticsService;
final NetworkService _networkService;
ProjectProvider(
this._podService,
this._graphService,
this._gitlabService,
this._analyticsService,
this._networkService,
);
List<String> notSubmittedLabelValues = [];
......@@ -69,7 +72,7 @@ class ProjectProvider with ChangeNotifier {
List<String> selectedFeatures = [];
Map<String, bool> _isProjectsTileExpanded = {};
Map<String, List<bool>> projectStepHover = {};
Map<String, int> selectedProjectUITemplate = {};
Map<String, InterfaceTemplate> selectedInterfaceTemplates = {};
bool get hasProjectPipelineError {
bool hasError = false;
......@@ -81,6 +84,8 @@ class ProjectProvider with ChangeNotifier {
return hasError;
}
String get baseUrl => _gitlabService.baseUrl;
bool isProjectsTileExpanded(String projectId) =>
_isProjectsTileExpanded.keys.contains(projectId) &&
_isProjectsTileExpanded[projectId]!;
......@@ -103,7 +108,7 @@ class ProjectProvider with ChangeNotifier {
datasetSummaries = {};
_isProjectsTileExpanded = {};
projectStepHover = {};
selectedProjectUITemplate = {};
selectedInterfaceTemplates = {};
}
resetProjectState() {
......@@ -569,7 +574,7 @@ class ProjectProvider with ChangeNotifier {
_analyticsService.logProjectTrainModel();
String? projectId = currentProject?.get('id');
RouteNavigator.navigateTo(
route: Routes.projectsAppPreview,
route: Routes.projectsPreviewApp,
context: context,
param: {'id': projectId});
}
......@@ -855,12 +860,12 @@ class ProjectProvider with ChangeNotifier {
void onTapGoogleColabButton() {
_analyticsService.logProjectGoogleColab();
js.context.callMethod('open', [app.settings.colabLink]);
_networkService.openLink(app.settings.colabLink, isNewTab: true);
}
void onTapProjectPipeline(String? jobUrl) {
_analyticsService.logProjectGitlabPipelineUrl(jobUrl);
js.context.callMethod('open', [jobUrl]);
_networkService.openLink(jobUrl ?? '', isNewTab: true);
}
void openProjectTile(String projectId) {
......@@ -895,10 +900,30 @@ class ProjectProvider with ChangeNotifier {
projectStepHover[projectId]!.elementAt(index);
}
void setSelectedProjectUITemplate(String projectId, int index) {
selectedProjectUITemplate[projectId] = index;
void setSelectedInterfaceTemplate(
String projectId, InterfaceTemplate template) {
selectedInterfaceTemplates[projectId] = template;
notifyListeners();
}
void onTapPreviewTemplate(
BuildContext context, ProjectProvider provider, int index) {
String template = app.projectTemplates.keys.elementAt(index).name;
if (kIsWeb) {
_networkService.openLink(
provider.baseUrl +
Routes.projectsPreviewTemplate +
'?template=$template',
isNewTab: true,
);
} else {
RouteNavigator.navigateTo(
route: Routes.projectsPreviewTemplate,
context: context,
param: {'template': template},
);
}
}
}
class DatasetSummary {
......
......@@ -54,9 +54,15 @@ class Apps extends StatefulWidget {
}
class _AppsState extends State<Apps> {
late double width = MediaQuery.of(context).size.width;
final double threshold = 1510;
double endPadding = 0;
@override
Widget build(BuildContext context) {
return Consumer<InboxProvider>(builder: (_, inboxProvider, __) {
width = MediaQuery.of(context).size.width;
endPadding = width > threshold ? width - threshold : 0;
return SingleChildScrollView(
child: Container(
padding:
......@@ -75,9 +81,9 @@ class _AppsState extends State<Apps> {
children: [
Text('All apps', style: app.styles.headline1),
SizedBox(height: 18),
if (whatsappExist && provider.dataApps.isNotEmpty)
if (whatsappExist && provider.anyDataAppExist)
_buildInboxTile(inboxProvider),
if (twitterExist && provider.dataApps.isNotEmpty)
if (twitterExist && provider.anyDataAppExist)
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: _buildFeedsTile(inboxProvider),
......@@ -94,7 +100,7 @@ class _AppsState extends State<Apps> {
)
else if (provider.appListState ==
AppListState.fetched &&
provider.dataApps.isNotEmpty)
provider.anyDataAppExist)
Column(
children: [
_buildAppListView(provider, inboxProvider,
......@@ -103,17 +109,17 @@ class _AppsState extends State<Apps> {
],
),
HelpTiles(),
if (whatsappExist && provider.dataApps.isEmpty)
if (whatsappExist && !provider.anyDataAppExist)
Padding(
padding: const EdgeInsets.only(top: 8),
child: _buildInboxTile(inboxProvider),
),
if (twitterExist && provider.dataApps.isEmpty)
if (twitterExist && !provider.anyDataAppExist)
Padding(
padding: const EdgeInsets.only(top: 8),
child: _buildFeedsTile(inboxProvider),
),
if (provider.dataApps.isEmpty)
if (!provider.anyDataAppExist)
_buildNewAppText(context, importerProvider),
],
);
......@@ -125,21 +131,27 @@ class _AppsState extends State<Apps> {
}
Widget _buildFeedsTile(InboxProvider inboxProvider) {
return InboxTile(
title: 'Feeds',
description:
'This is the default Feed of all posts sent via feed-driven data sources.',
onTap: () => inboxProvider.openInbox(DataSource.twitter),
return Padding(
padding: EdgeInsets.only(top: 8, right: endPadding),
child: InboxTile(
title: 'Feeds',
description:
'This is the default Feed of all posts sent via feed-driven data sources.',
onTap: () => inboxProvider.openInbox(DataSource.twitter),
),
);
}
Widget _buildInboxTile(InboxProvider inboxProvider) {
return Consumer<DataAppProvider>(builder: (_, daProvider, __) {
return InboxTile(
title: 'Inbox',
description:
'This app shows your messages labeled with sentiment and polarity.',
onTap: () => inboxProvider.openInbox(DataSource.whatsapp),
return Padding(
padding: EdgeInsets.only(top: 8, right: endPadding),
child: InboxTile(
title: 'Inbox',
description:
'This app shows your messages labeled with sentiment and polarity.',
onTap: () => inboxProvider.openInbox(DataSource.whatsapp),
),
);
});
}
......@@ -174,7 +186,6 @@ class _AppsState extends State<Apps> {
Widget _buildAppListView(DataAppProvider provider, InboxProvider iProvider,
{bool showInbox = false}) {
final width = MediaQuery.of(context).size.width;
List<Item> data =
showInbox ? provider.dataAppsInboxList : provider.dataApps;
return ListView.builder(
......@@ -182,8 +193,7 @@ class _AppsState extends State<Apps> {
shrinkWrap: true,
reverse: true,
itemBuilder: (context, index) => Padding(
padding:
EdgeInsets.only(top: 8, right: width > 1510 ? width - 1510 : 0),
padding: EdgeInsets.only(top: 8, right: endPadding),
child: showInbox
? _buildDataAppInboxTile(iProvider, data[index])
: DataAppTile(dataApp: data[index]),
......
......@@ -243,7 +243,7 @@ class _ProjectsLabelDataScreenState extends State<ProjectsLabelDataScreen> {
),
padding: const EdgeInsets.all(20),
child: Text(
currentItem.get('content'),
currentItem.get('content') ?? '[NO CONTENT]',
style: app.styles.headline2
.copyWith(color: app.theme.scaffoldBackgroundColor),
maxLines: ResponsiveHelper(context).isSmallScreen ? 5 : null,
......
......@@ -7,24 +7,24 @@ import 'package:memri/utilities/helpers/app_helper.dart';
import 'package:memri/utilities/helpers/responsive_helper.dart';
import 'package:memri/widgets/buttons/primary_page_button.dart';
import 'package:memri/widgets/buttons/secondary_page_button.dart';
import 'package:memri/widgets/data_app/data_app_list.dart';
import 'package:memri/widgets/data_app/data_app_logs.dart';
import 'package:memri/widgets/loading_indicator.dart';
import 'package:memri/widgets/scaffold/workspace_scaffold.dart';
import 'package:memri/widgets/sidebar/project_sidebar.dart';
import 'package:memri/widgets/templates/tamplates_table.dart';
import 'package:provider/provider.dart';
class ProjectsAppPreviewScreen extends StatefulWidget {
const ProjectsAppPreviewScreen({Key? key, required this.id})
class ProjectsPreviewAppScreen extends StatefulWidget {
const ProjectsPreviewAppScreen({Key? key, required this.id})
: super(key: key);
final String id;
@override
State<ProjectsAppPreviewScreen> createState() => _ProjectsAppPreviewScreen();
State<ProjectsPreviewAppScreen> createState() => _ProjectsAppPreviewScreen();
}
class _ProjectsAppPreviewScreen extends State<ProjectsAppPreviewScreen> {
class _ProjectsAppPreviewScreen extends State<ProjectsPreviewAppScreen> {
late final dataAppProvider =
Provider.of<DataAppProvider>(context, listen: false);
List<String> contents = ['content 1', 'content 2', 'content 3'];
......@@ -83,7 +83,7 @@ class _ProjectsAppPreviewScreen extends State<ProjectsAppPreviewScreen> {
child: WorkspaceScaffold(
currentItem: NavigationItem.projects,
child: ProjectSidebar(
projectId: widget.id, currentStep: 4, child: _buildBody()),
projectId: widget.id, currentStep: 3, child: _buildBody()),
),
);
}
......@@ -110,134 +110,122 @@ class _ProjectsAppPreviewScreen extends State<ProjectsAppPreviewScreen> {
});
Widget _buildPreview(DataAppProvider dataAppProvider) {
return Consumer<ProjectProvider>(
builder: (context, projectProvider, _) {
int? selectedTemplate =
projectProvider.selectedProjectUITemplate[widget.id];
return SingleChildScrollView(
child: Row(
children: [
Expanded(
flex: 2,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 30),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
return SingleChildScrollView(
child: Row(
children: [
Expanded(
flex: 2,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 30),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (!ResponsiveHelper(context).isSmallScreen)
SizedBox(height: 20),
Text(
'Preview your predictions',
style: app.styles.headline1,
),
SizedBox(height: 10),
Text(
'Your model is now deployed! Check below a sample of your data with the predictions.',
style: app.styles.bodyText1.copyWith(fontSize: 16),
),
SizedBox(height: 50),
Row(
children: [
if (!ResponsiveHelper(context).isSmallScreen)
SizedBox(height: 20),
Text(
'Preview your predictions',
style: app.styles.headline1,
InkWell(
onTap: () => dataAppProvider.setDataAppTab(AppTab.app),
child: Text(
'App',
style: app.styles.buttonLabel.copyWith(
color: dataAppProvider.currentAppTab == AppTab.app
? app.colors.text
: app.colors.primary),
),
),
SizedBox(height: 10),
Text(
'Your model is now deployed! Check below a sample of your data with the predictions.',
style: app.styles.bodyText1.copyWith(fontSize: 16),
SizedBox(width: 20),
InkWell(
onTap: () => dataAppProvider.setDataAppTab(AppTab.logs),
child: Text(
'Logs',
style: app.styles.buttonLabel.copyWith(
color: dataAppProvider.currentAppTab == AppTab.app
? app.colors.text
: app.colors.primary),
),
),
SizedBox(height: 50),
Row(
children: [
InkWell(
onTap: () =>
dataAppProvider.setDataAppTab(AppTab.app),
child: Text(
'App',
style: app.styles.buttonLabel.copyWith(
color: dataAppProvider.currentAppTab ==
AppTab.app
? app.colors.text
: app.colors.primary),
),
),
SizedBox(width: 20),
InkWell(
onTap: () =>
dataAppProvider.setDataAppTab(AppTab.logs),
child: Text(
'Logs',
style: app.styles.buttonLabel.copyWith(
color: dataAppProvider.currentAppTab ==
AppTab.app
? app.colors.text
: app.colors.primary),
),
),
Spacer(),
dataAppProvider.pluginIsNotRunning()
? TextButton(
style: app.styles.tertiaryButtonStyle
.copyWith(
backgroundColor:
MaterialStateProperty.all(
app.colors.text)),
onPressed: () =>
dataAppProvider.runIndexerPluginFromGit(
project:
projectProvider.currentProject!),
child: Text(
'Run app',
style: app.styles.buttonLabel
.copyWith(color: app.colors.background),
),
)
: Text(
'Running...',
Spacer(),
dataAppProvider.pluginIsNotRunning()
? Consumer<ProjectProvider>(
builder: (context, projectProvider, _) {
return TextButton(
style: app.styles.tertiaryButtonStyle.copyWith(
backgroundColor: MaterialStateProperty.all(
app.colors.text)),
onPressed: () =>
dataAppProvider.runIndexerPluginFromGit(
project:
projectProvider.currentProject!),
child: Text(
'Run app',
style: app.styles.buttonLabel
.copyWith(color: app.colors.primary),
.copyWith(color: app.colors.background),
),
],
),
SizedBox(height: 20),
// data app
if (dataAppProvider.currentAppTab == AppTab.logs)
//logs
DataAppLogs(
currentRunLog: dataAppProvider.currentRunLog)
else if (dataAppProvider.currentAppTab == AppTab.app)
TemplatesTable(),
// if (selectedTemplate == 0)
// TemplatesTable()
// else if (selectedTemplate == 1)
// TemplatesVisualization()
// else if (selectedTemplate == 2)
// TemplatesInbox(),
SizedBox(height: 50),
Wrap(
children: [
PrimaryPageButton(
title: 'Publish',
onPressed: () => dataAppProvider.handlePublishApp(
context,
projectProvider.currentProject!,
projectProvider.dataSourceSelected.toString(),
selectedTemplate),
),
SizedBox(width: 20),
SecondaryPageButton(
title: 'Select UI template',
onPressed: () => RouteNavigator.navigateTo(
route: Routes.projectsSelectTemplate,
context: context,
param: {'id': widget.id},
replace: true,
);
})
: Text(
'Running...',
style: app.styles.buttonLabel
.copyWith(color: app.colors.primary),
),
),
],
],
),
SizedBox(height: 20),
// data app
if (dataAppProvider.currentAppTab == AppTab.logs)
//logs
DataAppLogs(currentRunLog: dataAppProvider.currentRunLog)
else if (dataAppProvider.currentAppTab == AppTab.app)
Container(
width: MediaQuery.of(context).size.width,
constraints: BoxConstraints(minHeight: 256),
child: DataAppList(),
),
SizedBox(height: 50),
Wrap(
children: [
PrimaryPageButton(
title: 'Select UI template',
onPressed: () => RouteNavigator.navigateTo(
route: Routes.projectsSelectTemplate,
context: context,
param: {'id': widget.id},
),
),
SizedBox(width: 20),
SecondaryPageButton(
title: 'Train a ML model',
onPressed: () => RouteNavigator.navigateTo(
route: Routes.projectsTrainModel,
context: context,
param: {'id': widget.id},
replace: true,
),
),
SizedBox(height: 20),
if (!ResponsiveHelper(context).isSmallScreen)
SizedBox(height: 20),
],
),
),
SizedBox(height: 20),
if (!ResponsiveHelper(context).isSmallScreen)
SizedBox(height: 20),
],
),
if (!ResponsiveHelper(context).isSmallScreen)
Expanded(child: Column()),
],
),
),
);
},
if (!ResponsiveHelper(context).isSmallScreen)
Expanded(child: Column()),
],
),
);
}
......
import 'package:flutter/material.dart';
import 'package:memri/utilities/extensions/string.dart';
import 'package:memri/widgets/scaffold/workspace_scaffold.dart';
class ProjectsPreviewTemplate extends StatefulWidget {
const ProjectsPreviewTemplate({Key? key, this.template}) : super(key: key);
final String? template;
@override
State<ProjectsPreviewTemplate> createState() =>
_ProjectsPreviewTemplateState();
}
class _ProjectsPreviewTemplateState extends State<ProjectsPreviewTemplate> {
@override
Widget build(BuildContext context) {
return WorkspaceScaffold(
currentItem: NavigationItem.projects,
child: Center(
child: Text(
(widget.template ?? 'Select a template...!').capitalize()), // TODO
),
);
}
}
import 'package:flutter/material.dart';
import 'package:memri/configs/routes/route_navigator.dart';
import 'package:memri/providers/data_app_provider.dart';
import 'package:memri/providers/project_provider.dart';
import 'package:memri/utilities/helpers/app_helper.dart';
import 'package:memri/utilities/helpers/responsive_helper.dart';
import 'package:memri/widgets/buttons/primary_page_button.dart';
import 'package:memri/widgets/buttons/secondary_button.dart';
import 'package:memri/widgets/buttons/secondary_page_button.dart';
import 'package:memri/widgets/scaffold/workspace_scaffold.dart';
import 'package:memri/widgets/sidebar/project_sidebar.dart';
......@@ -22,11 +24,14 @@ class ProjectsSelectTemplateScreen extends StatefulWidget {
class _ProjectsSelectTemplateScreenState
extends State<ProjectsSelectTemplateScreen> {
late final pProvider = Provider.of<ProjectProvider>(context, listen: false);
late final _pProvider = Provider.of<ProjectProvider>(context, listen: false);
late final _daProvider = Provider.of<DataAppProvider>(context, listen: false);
@override
void initState() {
pProvider.selectedProjectUITemplate[widget.id] = 0;
_pProvider.selectedInterfaceTemplates[widget.id] ??=
InterfaceTemplate.table;
_daProvider.currentGitProjectId = _pProvider.gitlabProjectId;
super.initState();
}
......@@ -35,7 +40,7 @@ class _ProjectsSelectTemplateScreenState
return WorkspaceScaffold(
currentItem: NavigationItem.projects,
child: ProjectSidebar(
projectId: widget.id, currentStep: 3, child: _buildBody()),
projectId: widget.id, currentStep: 4, child: _buildBody()),
);
}
......@@ -43,6 +48,9 @@ class _ProjectsSelectTemplateScreenState
return SingleChildScrollView(
child: Consumer<ProjectProvider>(
builder: (context, provider, _) {
InterfaceTemplate selectedTemplate =
provider.selectedInterfaceTemplates[widget.id] ??
InterfaceTemplate.table;
return Container(
width: MediaQuery.of(context).size.width,
padding: EdgeInsets.symmetric(horizontal: 30),
......@@ -73,13 +81,25 @@ class _ProjectsSelectTemplateScreenState
children: List.generate(
app.projectTemplates.length,
(index) {
bool selected =
provider.selectedProjectUITemplate[widget.id] ==
index;
bool selected;
switch (selectedTemplate) {
case InterfaceTemplate.table:
selected = index == 0;
break;
case InterfaceTemplate.visualization:
selected = index == 1;
break;
case InterfaceTemplate.inbox:
selected = index == 2;
break;
default:
selected = false;
break;
}
return InkWell(
onTap: () {
provider.setSelectedProjectUITemplate(
widget.id, index);
provider.setSelectedInterfaceTemplate(widget.id,
app.projectTemplates.keys.elementAt(index));
},
child: Container(
height: selected ? 158 : 105,
......@@ -101,7 +121,8 @@ class _ProjectsSelectTemplateScreenState
children: [
Text(
app.projectTemplates.keys
.elementAt(index),
.elementAt(index)
.title,
style: app.styles.headline3.copyWith(
color: app.colors.primary),
),
......@@ -121,6 +142,15 @@ class _ProjectsSelectTemplateScreenState
style: app.styles.bodyTiny
.copyWith(color: app.colors.textGrey),
),
if (selected) Spacer(),
if (selected)
SecondaryButton(
title: 'Preview template',
padding: EdgeInsets.zero,
onPressed: () =>
provider.onTapPreviewTemplate(
context, provider, index),
),
],
),
),
......@@ -135,17 +165,17 @@ class _ProjectsSelectTemplateScreenState
Wrap(
children: [
PrimaryPageButton(
title: 'Preview prediction',
onPressed: provider.projectCIPassed &&
provider.projectHasPackage
? () => provider.handleCreateApp(context)
: null,
title: 'Publish',
onPressed: () => _daProvider.handlePublishApp(
context,
provider.currentProject!,
provider.dataSourceSelected.toString()),
),
SizedBox(width: 20),
SecondaryPageButton(
title: 'Train a ML model',
title: 'Preview predictions',
onPressed: () => RouteNavigator.navigateTo(
route: Routes.projectsTrainModel,
route: Routes.projectsPreviewApp,
context: context,
param: {'id': widget.id},
replace: true,
......
......@@ -150,16 +150,12 @@ class _ProjectsTrainModelScreenState extends State<ProjectsTrainModelScreen> {
Wrap(
children: [
PrimaryPageButton(
title: 'Select UI template',
title: 'Preview predictions',
inactive: !(provider.projectCIPassed &&
provider.projectHasPackage),
onPressed: provider.projectCIPassed &&
provider.projectHasPackage
? () => RouteNavigator.navigateTo(
route: Routes.projectsSelectTemplate,
context: context,
param: {'id': widget.id},
)
? () => provider.handleCreateApp(context)
: null,
),
SizedBox(width: 20),
......
......@@ -49,17 +49,33 @@ class ApplicationHelper {
'Project setup',
'Label data',
'Train a ML model',
'Select UI template',
'Preview predictions',
'Select UI template',
];
final Map<String, String> projectTemplates = {
'Table': 'This template shows items with labels in a simple table.',
'Visualization':
final Map<InterfaceTemplate, String> projectTemplates = {
InterfaceTemplate.table:
'This template shows items with labels in a simple table.',
InterfaceTemplate.visualization:
'This template allows to view your data as interactive graphs and charts.',
'Inbox Filters':
InterfaceTemplate.inbox:
'This template turns your labels into filters in your inbox.',
};
}
ApplicationHelper get app => ApplicationHelper();
enum InterfaceTemplate { table, visualization, inbox }
extension ParseToString on InterfaceTemplate {
String get title {
switch (this) {
case InterfaceTemplate.table:
return 'Table';
case InterfaceTemplate.visualization:
return 'Visualization';
case InterfaceTemplate.inbox:
return 'Inbox Filters';
}
}
}
......@@ -2,25 +2,28 @@ import 'package:flutter/material.dart';
import 'package:memri/utilities/helpers/app_helper.dart';
class SecondaryButton extends StatelessWidget {
const SecondaryButton(
{Key? key,
required this.title,
this.onPressed,
this.showLabel = true,
this.color})
: super(key: key);
const SecondaryButton({
Key? key,
required this.title,
this.onPressed,
this.showLabel = true,
this.color,
this.padding,
}) : super(key: key);
final String title;
final VoidCallback? onPressed;
final bool showLabel;
final Color? color;
final EdgeInsetsGeometry? padding;
@override
Widget build(BuildContext context) {
return TextButton(
onPressed: onPressed,
style: TextButton.styleFrom(
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 8.5),
padding:
padding ?? EdgeInsets.symmetric(horizontal: 10, vertical: 8.5),
minimumSize: Size.zero,
backgroundColor: Colors.transparent),
child: Text(title,
......
......@@ -4,7 +4,6 @@ import 'package:memri/core/models/running_plugin.dart';
import 'package:memri/providers/data_app_provider.dart';
import 'package:memri/utilities/helpers/app_helper.dart';
import 'package:memri/utilities/helpers/responsive_helper.dart';
import 'package:memri/widgets/data_app/data_app_list.dart';
import 'package:memri/widgets/loading_indicator.dart';
import 'package:provider/provider.dart';
......@@ -70,7 +69,7 @@ class _DataAppState extends State<DataApp> {
),
),
SizedBox(height: 50),
DataAppList(),
dataAppProvider.currentDataAppTemplate,
],
),
),
......
......@@ -196,7 +196,7 @@ class _InboxSidebarState extends State<InboxSidebar> {
padding:
const EdgeInsets.symmetric(horizontal: 15, vertical: 8.5),
child: Text(
label.replaceFirst(label[0], label[0].toUpperCase()),
label.capitalize(),
style:
app.styles.nameStyle.copyWith(fontSize: 15, color: color),
),
......
......@@ -71,7 +71,11 @@ class ProjectSidebar extends StatelessWidget {
itemBuilder: (context, index) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 20),
child: ProjectStepsListTile(
index: index, projectId: projectId, activeIndex: currentStep),
index: index,
projectId: projectId,
activeIndex: currentStep,
summary: summary,
),
),
),
),
......@@ -95,9 +99,10 @@ class ProjectSidebar extends StatelessWidget {
itemBuilder: (context, index) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: ProjectStepsListTile(
index: index,
projectId: projectId,
activeIndex: currentStep),
index: index,
projectId: projectId,
activeIndex: currentStep,
),
),
),
),
......
import 'package:flutter/material.dart';
class TemplatesInbox extends StatefulWidget {
const TemplatesInbox({Key? key}) : super(key: key);
@override
State<TemplatesInbox> createState() => _TemplatesInboxState();
}
class _TemplatesInboxState extends State<TemplatesInbox> {
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Container(
width: MediaQuery.of(context).size.width,
constraints: BoxConstraints(minHeight: 256),
),
);
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment