mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-17 02:25:31 +00:00
Compare commits
40 Commits
42c410d982
...
firebase_p
Author | SHA1 | Date | |
---|---|---|---|
df29aab111 | |||
138390496c | |||
46a7add90d | |||
73de1e6ff9 | |||
826dea8054 | |||
fdea4b1cd0 | |||
823d86fd80 | |||
dd735032ea | |||
6dcc851d97 | |||
15b36fd052 | |||
a4024067c7 | |||
95cded4bf5 | |||
757a96ed9f | |||
b857736e10 | |||
1fccd51440 | |||
c07ddb0ccd | |||
58e99f95b2 | |||
227df6fe3d | |||
9451ec0cc4 | |||
fc797c2646 | |||
318e1d9af7 | |||
d47dc349bc | |||
c221c8499f | |||
71cf4b9feb | |||
c43cf9347f | |||
9990b1805e | |||
009b7c0316 | |||
779c0fe916 | |||
e448eabda6 | |||
9dfb3ed369 | |||
63353af38b | |||
68b6c9b18c | |||
fa6ee9a0af | |||
3601b02bc3 | |||
fdd0526c78 | |||
bdeec7d325 | |||
50ff17a0c1 | |||
87c2e3261d | |||
62a6f9c993 | |||
f7e4d6ff07 |
@ -1,2 +1,3 @@
|
|||||||
ENV_NAME=development
|
ENV_NAME=development
|
||||||
BASE_URL=https://syncrow-dev.azurewebsites.net
|
BASE_URL=https://syncrow-dev.azurewebsites.net
|
||||||
|
RTDB_URL=https://syncrow-dev-79446.asia-southeast1.firebasedatabase.app/
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
ENV_NAME=production
|
ENV_NAME=production
|
||||||
BASE_URL=https://syncrow-staging.azurewebsites.net
|
BASE_URL=https://syncrow-staging.azurewebsites.net
|
||||||
|
RTDB_URL=https://syncrow-prod-79446.asia-southeast1.firebasedatabase.app/
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
ENV_NAME=staging
|
ENV_NAME=staging
|
||||||
BASE_URL=https://syncrow-staging.azurewebsites.net
|
BASE_URL=https://syncrow-staging.azurewebsites.net
|
||||||
|
RTDB_URL=https://syncrow-staging-79446.asia-southeast1.firebasedatabase.app/
|
||||||
|
112
.vscode/launch.json
vendored
112
.vscode/launch.json
vendored
@ -1,67 +1,49 @@
|
|||||||
{
|
{
|
||||||
"configurations": [
|
"configurations": [
|
||||||
|
{
|
||||||
{
|
"name": "DEVELOPMENT",
|
||||||
|
"request": "launch",
|
||||||
"name": "DEVELOPMENT",
|
"type": "dart",
|
||||||
|
"args": [
|
||||||
"request": "launch",
|
"-d",
|
||||||
|
"chrome",
|
||||||
"type": "dart",
|
"--web-port",
|
||||||
|
"3000",
|
||||||
"args": [
|
"-t",
|
||||||
"-d",
|
"lib/main_dev.dart",
|
||||||
"chrome",
|
"--web-experimental-hot-reload"
|
||||||
"--web-port",
|
],
|
||||||
"3000",
|
"flutterMode": "debug"
|
||||||
"-t",
|
},
|
||||||
"lib/main_dev.dart",
|
{
|
||||||
"--web-experimental-hot-reload",
|
"name": "STAGING",
|
||||||
],
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
"flutterMode": "debug"
|
"args": [
|
||||||
|
"-d",
|
||||||
},{
|
"chrome",
|
||||||
|
"--web-port",
|
||||||
"name": "STAGING",
|
"3000",
|
||||||
|
"-t",
|
||||||
"request": "launch",
|
"lib/main_staging.dart",
|
||||||
|
"--web-experimental-hot-reload"
|
||||||
"type": "dart",
|
],
|
||||||
|
"flutterMode": "debug"
|
||||||
"args": [
|
},
|
||||||
"-d",
|
{
|
||||||
"chrome",
|
"name": "PRODUCTION",
|
||||||
"--web-port",
|
"request": "launch",
|
||||||
"3000",
|
"type": "dart",
|
||||||
"-t",
|
"args": [
|
||||||
"lib/main_staging.dart",
|
"-d",
|
||||||
"--web-experimental-hot-reload",
|
"chrome",
|
||||||
],
|
"--web-port",
|
||||||
|
"3000",
|
||||||
"flutterMode": "debug"
|
"-t",
|
||||||
|
"lib/main.dart",
|
||||||
},{
|
"--web-experimental-hot-reload"
|
||||||
|
],
|
||||||
"name": "PRODUCTION",
|
"flutterMode": "debug"
|
||||||
|
}
|
||||||
"request": "launch",
|
]
|
||||||
|
|
||||||
"type": "dart",
|
|
||||||
|
|
||||||
"args": [
|
|
||||||
"-d",
|
|
||||||
"chrome",
|
|
||||||
"--web-port",
|
|
||||||
"3000",
|
|
||||||
"-t",
|
|
||||||
"lib/main.dart",
|
|
||||||
"--web-experimental-hot-reload",
|
|
||||||
],
|
|
||||||
|
|
||||||
"flutterMode": "debug"
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
]
|
|
||||||
}
|
}
|
@ -1 +1 @@
|
|||||||
{"flutter":{"platforms":{"android":{"default":{"projectId":"test2-8a3d2","appId":"1:427332280600:android:2bc36fbe82994a3e0c7e6d","fileOutput":"android/app/google-services.json"}},"ios":{"default":{"projectId":"test2-8a3d2","appId":"1:427332280600:ios:14346b200780dc760c7e6d","uploadDebugSymbols":true,"fileOutput":"ios/Runner/GoogleService-Info.plist"}},"macos":{"default":{"projectId":"test2-8a3d2","appId":"1:427332280600:ios:14346b200780dc760c7e6d","uploadDebugSymbols":true,"fileOutput":"macos/Runner/GoogleService-Info.plist"}},"dart":{"lib/firebase_options.dart":{"projectId":"test2-8a3d2","configurations":{"android":"1:427332280600:android:2bc36fbe82994a3e0c7e6d","ios":"1:427332280600:ios:14346b200780dc760c7e6d","macos":"1:427332280600:ios:14346b200780dc760c7e6d","web":"1:427332280600:web:ad50516a87a35a1a0c7e6d","windows":"1:427332280600:web:f7a25537ccd5a7bd0c7e6d"}}}}}}
|
{"flutter":{"platforms":{"android":{"default":{"projectId":"test2-8a3d2","appId":"1:427332280600:android:2bc36fbe82994a3e0c7e6d","fileOutput":"android/app/google-services.json"}},"ios":{"default":{"projectId":"test2-8a3d2","appId":"1:427332280600:ios:14346b200780dc760c7e6d","uploadDebugSymbols":true,"fileOutput":"ios/Runner/GoogleService-Info.plist"}},"macos":{"default":{"projectId":"test2-8a3d2","appId":"1:427332280600:ios:14346b200780dc760c7e6d","uploadDebugSymbols":true,"fileOutput":"macos/Runner/GoogleService-Info.plist"}},"dart":{"lib/firebase_options.dart":{"projectId":"syncrow-prod-79446","configurations":{"web":"1:255001682464:web:a03e2d6214c13101561245"}}}}}}
|
16
lib/firebase_options.dart
Normal file
16
lib/firebase_options.dart
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
|
||||||
|
|
||||||
|
final class DefaultFirebaseOptions extends FirebaseOptions {
|
||||||
|
const DefaultFirebaseOptions({
|
||||||
|
required String databaseUrl,
|
||||||
|
}) : super(
|
||||||
|
apiKey: 'AIzaSyDgq5ywsnFVbbQO-Xz1Z4sR5bBcuiDaS9g',
|
||||||
|
appId: '1:255001682464:web:a03e2d6214c13101561245',
|
||||||
|
messagingSenderId: '255001682464',
|
||||||
|
projectId: 'syncrow-prod-79446',
|
||||||
|
authDomain: 'syncrow-prod-79446.firebaseapp.com',
|
||||||
|
storageBucket: 'syncrow-prod-79446.firebasestorage.app',
|
||||||
|
databaseURL: databaseUrl,
|
||||||
|
measurementId: 'G-1850Q89RMK',
|
||||||
|
);
|
||||||
|
}
|
@ -1,93 +0,0 @@
|
|||||||
// File generated by FlutterFire CLI.
|
|
||||||
// ignore_for_file: type=lint
|
|
||||||
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
|
|
||||||
import 'package:flutter/foundation.dart'
|
|
||||||
show defaultTargetPlatform, kIsWeb, TargetPlatform;
|
|
||||||
|
|
||||||
/// Default [FirebaseOptions] for use with your Firebase apps.
|
|
||||||
///
|
|
||||||
/// Example:
|
|
||||||
/// ```dart
|
|
||||||
/// import 'firebase_options.dart';
|
|
||||||
/// // ...
|
|
||||||
/// await Firebase.initializeApp(
|
|
||||||
/// options: DefaultFirebaseOptions.currentPlatform,
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
class DefaultFirebaseOptionsDev {
|
|
||||||
static FirebaseOptions get currentPlatform {
|
|
||||||
if (kIsWeb) {
|
|
||||||
return web;
|
|
||||||
}
|
|
||||||
switch (defaultTargetPlatform) {
|
|
||||||
case TargetPlatform.android:
|
|
||||||
return android;
|
|
||||||
case TargetPlatform.iOS:
|
|
||||||
return ios;
|
|
||||||
case TargetPlatform.macOS:
|
|
||||||
return macos;
|
|
||||||
case TargetPlatform.windows:
|
|
||||||
return windows;
|
|
||||||
case TargetPlatform.linux:
|
|
||||||
throw UnsupportedError(
|
|
||||||
'DefaultFirebaseOptions have not been configured for linux - '
|
|
||||||
'you can reconfigure this by running the FlutterFire CLI again.',
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
throw UnsupportedError(
|
|
||||||
'DefaultFirebaseOptions are not supported for this platform.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const FirebaseOptions web = FirebaseOptions(
|
|
||||||
apiKey: 'AIzaSyCVEvKsJYzhWDFM-9Od68FE0nPpP933st0',
|
|
||||||
appId: '1:427332280600:web:ad50516a87a35a1a0c7e6d',
|
|
||||||
messagingSenderId: '427332280600',
|
|
||||||
projectId: 'test2-8a3d2',
|
|
||||||
authDomain: 'test2-8a3d2.firebaseapp.com',
|
|
||||||
databaseURL: 'https://test2-8a3d2-default-rtdb.firebaseio.com',
|
|
||||||
storageBucket: 'test2-8a3d2.firebasestorage.app',
|
|
||||||
measurementId: 'G-Z1RTTTV5H9',
|
|
||||||
);
|
|
||||||
|
|
||||||
static const FirebaseOptions android = FirebaseOptions(
|
|
||||||
apiKey: 'AIzaSyA5qOErxdm0zJmoHIB0TixfebYEsNRpwV0',
|
|
||||||
appId: '1:427332280600:android:2bc36fbe82994a3e0c7e6d',
|
|
||||||
messagingSenderId: '427332280600',
|
|
||||||
projectId: 'test2-8a3d2',
|
|
||||||
databaseURL: 'https://test2-8a3d2-default-rtdb.firebaseio.com',
|
|
||||||
storageBucket: 'test2-8a3d2.firebasestorage.app',
|
|
||||||
);
|
|
||||||
|
|
||||||
static const FirebaseOptions ios = FirebaseOptions(
|
|
||||||
apiKey: 'AIzaSyABnpH6yo2RRjtkp4PlvtK84hKwRm2DhBw',
|
|
||||||
appId: '1:427332280600:ios:14346b200780dc760c7e6d',
|
|
||||||
messagingSenderId: '427332280600',
|
|
||||||
projectId: 'test2-8a3d2',
|
|
||||||
databaseURL: 'https://test2-8a3d2-default-rtdb.firebaseio.com',
|
|
||||||
storageBucket: 'test2-8a3d2.firebasestorage.app',
|
|
||||||
iosBundleId: 'com.example.syncrowWeb',
|
|
||||||
);
|
|
||||||
|
|
||||||
static const FirebaseOptions macos = FirebaseOptions(
|
|
||||||
apiKey: 'AIzaSyABnpH6yo2RRjtkp4PlvtK84hKwRm2DhBw',
|
|
||||||
appId: '1:427332280600:ios:14346b200780dc760c7e6d',
|
|
||||||
messagingSenderId: '427332280600',
|
|
||||||
projectId: 'test2-8a3d2',
|
|
||||||
databaseURL: 'https://test2-8a3d2-default-rtdb.firebaseio.com',
|
|
||||||
storageBucket: 'test2-8a3d2.firebasestorage.app',
|
|
||||||
iosBundleId: 'com.example.syncrowWeb',
|
|
||||||
);
|
|
||||||
|
|
||||||
static const FirebaseOptions windows = FirebaseOptions(
|
|
||||||
apiKey: 'AIzaSyDizKjPC5rdkEjDxwXjM-RU5unB0Ziq3iw',
|
|
||||||
appId: '1:427332280600:web:f7a25537ccd5a7bd0c7e6d',
|
|
||||||
messagingSenderId: '427332280600',
|
|
||||||
projectId: 'test2-8a3d2',
|
|
||||||
authDomain: 'test2-8a3d2.firebaseapp.com',
|
|
||||||
databaseURL: 'https://test2-8a3d2-default-rtdb.firebaseio.com',
|
|
||||||
storageBucket: 'test2-8a3d2.firebasestorage.app',
|
|
||||||
measurementId: 'G-4LFVXEXWKY',
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,77 +0,0 @@
|
|||||||
// File generated by FlutterFire CLI.
|
|
||||||
// ignore_for_file: type=lint
|
|
||||||
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
|
|
||||||
import 'package:flutter/foundation.dart' show defaultTargetPlatform, kIsWeb, TargetPlatform;
|
|
||||||
|
|
||||||
/// Default [FirebaseOptions] for use with your Firebase apps.
|
|
||||||
///
|
|
||||||
/// Example:
|
|
||||||
/// ```dart
|
|
||||||
/// import 'firebase_options.dart';
|
|
||||||
/// // ...
|
|
||||||
/// await Firebase.initializeApp(
|
|
||||||
/// options: DefaultFirebaseOptions.currentPlatform,
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
class DefaultFirebaseOptionsStaging {
|
|
||||||
static FirebaseOptions get currentPlatform {
|
|
||||||
if (kIsWeb) {
|
|
||||||
return web;
|
|
||||||
}
|
|
||||||
switch (defaultTargetPlatform) {
|
|
||||||
case TargetPlatform.android:
|
|
||||||
return android;
|
|
||||||
case TargetPlatform.iOS:
|
|
||||||
return ios;
|
|
||||||
case TargetPlatform.macOS:
|
|
||||||
throw UnsupportedError(
|
|
||||||
'DefaultFirebaseOptions have not been configured for macos - '
|
|
||||||
'you can reconfigure this by running the FlutterFire CLI again.',
|
|
||||||
);
|
|
||||||
case TargetPlatform.windows:
|
|
||||||
throw UnsupportedError(
|
|
||||||
'DefaultFirebaseOptions have not been configured for windows - '
|
|
||||||
'you can reconfigure this by running the FlutterFire CLI again.',
|
|
||||||
);
|
|
||||||
case TargetPlatform.linux:
|
|
||||||
throw UnsupportedError(
|
|
||||||
'DefaultFirebaseOptions have not been configured for linux - '
|
|
||||||
'you can reconfigure this by running the FlutterFire CLI again.',
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
throw UnsupportedError(
|
|
||||||
'DefaultFirebaseOptions are not supported for this platform.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const FirebaseOptions android = FirebaseOptions(
|
|
||||||
apiKey: 'AIzaSyDP9GpYfLE8gHTj3kZ1hW8fx_FkJqOqSQk',
|
|
||||||
appId: '1:786692570726:android:0ef7079c2b978d4417b7a7',
|
|
||||||
messagingSenderId: '786692570726',
|
|
||||||
projectId: 'syncrow-staging',
|
|
||||||
databaseURL: 'https://syncrow-staging-default-rtdb.firebaseio.com',
|
|
||||||
storageBucket: 'syncrow-staging.appspot.com',
|
|
||||||
);
|
|
||||||
|
|
||||||
static const FirebaseOptions ios = FirebaseOptions(
|
|
||||||
apiKey: 'AIzaSyAWlRiuJ75FMlf2_UDdri1voWKvkaSHtRg',
|
|
||||||
appId: '1:786692570726:ios:455a6fcff77e130f17b7a7',
|
|
||||||
messagingSenderId: '786692570726',
|
|
||||||
projectId: 'syncrow-staging',
|
|
||||||
databaseURL: 'https://syncrow-staging-default-rtdb.firebaseio.com',
|
|
||||||
storageBucket: 'syncrow-staging.appspot.com',
|
|
||||||
iosBundleId: 'com.example.syncrow.app',
|
|
||||||
);
|
|
||||||
|
|
||||||
static const FirebaseOptions web = FirebaseOptions(
|
|
||||||
apiKey: 'AIzaSyDyGaQ3sZhb4meaY6sGke-YglhdhJ2is8Q',
|
|
||||||
appId: '1:786692570726:web:93c931e6701797b317b7a7',
|
|
||||||
messagingSenderId: '786692570726',
|
|
||||||
projectId: 'syncrow-staging',
|
|
||||||
authDomain: 'syncrow-staging.firebaseapp.com',
|
|
||||||
databaseURL: 'https://syncrow-staging-default-rtdb.firebaseio.com',
|
|
||||||
storageBucket: 'syncrow-staging.appspot.com',
|
|
||||||
measurementId: 'G-CZ3J3G6LMQ',
|
|
||||||
);
|
|
||||||
}
|
|
@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:syncrow_web/firebase_options_prod.dart';
|
import 'package:syncrow_web/firebase_options.dart';
|
||||||
import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart';
|
import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
|
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/home/bloc/home_event.dart';
|
import 'package:syncrow_web/pages/home/bloc/home_event.dart';
|
||||||
@ -27,7 +27,9 @@ Future<void> main() async {
|
|||||||
await dotenv.load(fileName: '.env.$environment');
|
await dotenv.load(fileName: '.env.$environment');
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
await Firebase.initializeApp(
|
await Firebase.initializeApp(
|
||||||
options: DefaultFirebaseOptionsStaging.currentPlatform,
|
options: DefaultFirebaseOptions(
|
||||||
|
databaseUrl: dotenv.env['RTDB_URL']!,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
initialSetup();
|
initialSetup();
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
@ -59,7 +61,7 @@ class MyApp extends StatelessWidget {
|
|||||||
BlocProvider<CreateRoutineBloc>(
|
BlocProvider<CreateRoutineBloc>(
|
||||||
create: (context) => CreateRoutineBloc(),
|
create: (context) => CreateRoutineBloc(),
|
||||||
),
|
),
|
||||||
BlocProvider(create: (context) => HomeBloc()..add(FetchUserInfo())),
|
BlocProvider(create: (context) => HomeBloc()..add(const FetchUserInfo())),
|
||||||
BlocProvider<VisitorPasswordBloc>(
|
BlocProvider<VisitorPasswordBloc>(
|
||||||
create: (context) => VisitorPasswordBloc(),
|
create: (context) => VisitorPasswordBloc(),
|
||||||
),
|
),
|
||||||
|
@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:syncrow_web/firebase_options_dev.dart';
|
import 'package:syncrow_web/firebase_options.dart';
|
||||||
import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart';
|
import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
|
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/home/bloc/home_event.dart';
|
import 'package:syncrow_web/pages/home/bloc/home_event.dart';
|
||||||
@ -27,7 +27,9 @@ Future<void> main() async {
|
|||||||
await dotenv.load(fileName: '.env.$environment');
|
await dotenv.load(fileName: '.env.$environment');
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
await Firebase.initializeApp(
|
await Firebase.initializeApp(
|
||||||
options: DefaultFirebaseOptionsDev.currentPlatform,
|
options: DefaultFirebaseOptions(
|
||||||
|
databaseUrl: dotenv.env['RTDB_URL']!,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
initialSetup();
|
initialSetup();
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
@ -59,7 +61,7 @@ class MyApp extends StatelessWidget {
|
|||||||
BlocProvider<CreateRoutineBloc>(
|
BlocProvider<CreateRoutineBloc>(
|
||||||
create: (context) => CreateRoutineBloc(),
|
create: (context) => CreateRoutineBloc(),
|
||||||
),
|
),
|
||||||
BlocProvider(create: (context) => HomeBloc()..add(FetchUserInfo())),
|
BlocProvider(create: (context) => HomeBloc()..add(const FetchUserInfo())),
|
||||||
BlocProvider<VisitorPasswordBloc>(
|
BlocProvider<VisitorPasswordBloc>(
|
||||||
create: (context) => VisitorPasswordBloc(),
|
create: (context) => VisitorPasswordBloc(),
|
||||||
),
|
),
|
||||||
|
@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:syncrow_web/firebase_options_prod.dart';
|
import 'package:syncrow_web/firebase_options.dart';
|
||||||
import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart';
|
import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
|
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/home/bloc/home_event.dart';
|
import 'package:syncrow_web/pages/home/bloc/home_event.dart';
|
||||||
@ -24,7 +24,9 @@ Future<void> main() async {
|
|||||||
await dotenv.load(fileName: '.env.$environment');
|
await dotenv.load(fileName: '.env.$environment');
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
await Firebase.initializeApp(
|
await Firebase.initializeApp(
|
||||||
options: DefaultFirebaseOptionsStaging.currentPlatform,
|
options: DefaultFirebaseOptions(
|
||||||
|
databaseUrl: dotenv.env['RTDB_URL']!,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
initialSetup();
|
initialSetup();
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
@ -56,7 +58,7 @@ class MyApp extends StatelessWidget {
|
|||||||
BlocProvider<CreateRoutineBloc>(
|
BlocProvider<CreateRoutineBloc>(
|
||||||
create: (context) => CreateRoutineBloc(),
|
create: (context) => CreateRoutineBloc(),
|
||||||
),
|
),
|
||||||
BlocProvider(create: (context) => HomeBloc()..add(FetchUserInfo())),
|
BlocProvider(create: (context) => HomeBloc()..add(const FetchUserInfo())),
|
||||||
BlocProvider<VisitorPasswordBloc>(
|
BlocProvider<VisitorPasswordBloc>(
|
||||||
create: (context) => VisitorPasswordBloc(),
|
create: (context) => VisitorPasswordBloc(),
|
||||||
),
|
),
|
||||||
|
@ -105,7 +105,7 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
|||||||
color: const Color(0xFF0026A2),
|
color: const Color(0xFF0026A2),
|
||||||
),
|
),
|
||||||
HomeItemModel(
|
HomeItemModel(
|
||||||
title: 'Devices Management',
|
title: 'Device Management',
|
||||||
icon: Assets.devicesIcon,
|
icon: Assets.devicesIcon,
|
||||||
active: true,
|
active: true,
|
||||||
onPress: (context) {
|
onPress: (context) {
|
||||||
|
@ -1,24 +1,39 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/bloc/communities_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/communities_tree_selection_bloc/communities_tree_selection_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/create_community/presentation/create_community_dialog.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/create_community/presentation/create_community_dialog.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/update_community/presentation/edit_community_dialog.dart';
|
||||||
|
|
||||||
abstract final class SpaceManagementCommunityDialogHelper {
|
abstract final class SpaceManagementCommunityDialogHelper {
|
||||||
static void showCreateDialog(BuildContext context) {
|
static void showCreateDialog(BuildContext context) => showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (_) => const CreateCommunityDialog(),
|
||||||
|
);
|
||||||
|
|
||||||
|
static void showEditDialog(
|
||||||
|
BuildContext context,
|
||||||
|
CommunityModel community,
|
||||||
|
) {
|
||||||
showDialog<void>(
|
showDialog<void>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (_) => CreateCommunityDialog(
|
builder: (_) => EditCommunityDialog(
|
||||||
title: const SelectableText('Community Name'),
|
community: community,
|
||||||
onCreateCommunity: (community) {
|
parentContext: context,
|
||||||
context.read<CommunitiesBloc>().add(
|
|
||||||
InsertCommunity(community),
|
|
||||||
);
|
|
||||||
context.read<CommunitiesTreeSelectionBloc>().add(
|
|
||||||
SelectCommunityEvent(community: community),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void showLoadingDialog(BuildContext context) => showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: false,
|
||||||
|
builder: (context) => const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
static void showSuccessSnackBar(BuildContext context, String message) =>
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text(message),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,28 +1,29 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
|
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
|
||||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/create_community/domain/param/create_community_param.dart';
|
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/create_community/presentation/bloc/create_community_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/create_community/presentation/create_community_name_text_field.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/create_community/presentation/create_community_name_text_field.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
class CreateCommunityDialogWidget extends StatefulWidget {
|
class CommunityDialog extends StatefulWidget {
|
||||||
final String? initialName;
|
final String? initialName;
|
||||||
final Widget title;
|
final Widget title;
|
||||||
|
final void Function(String name) onSubmit;
|
||||||
|
final String? errorMessage;
|
||||||
|
|
||||||
const CreateCommunityDialogWidget({
|
const CommunityDialog({
|
||||||
super.key,
|
|
||||||
required this.title,
|
required this.title,
|
||||||
|
required this.onSubmit,
|
||||||
this.initialName,
|
this.initialName,
|
||||||
|
this.errorMessage,
|
||||||
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<CreateCommunityDialogWidget> createState() =>
|
State<CommunityDialog> createState() => _CommunityDialogState();
|
||||||
_CreateCommunityDialogWidgetState();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _CreateCommunityDialogWidgetState extends State<CreateCommunityDialogWidget> {
|
class _CommunityDialogState extends State<CommunityDialog> {
|
||||||
late final TextEditingController _nameController;
|
late final TextEditingController _nameController;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -63,35 +64,20 @@ class _CreateCommunityDialogWidgetState extends State<CreateCommunityDialogWidge
|
|||||||
child: Form(
|
child: Form(
|
||||||
key: _formKey,
|
key: _formKey,
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
child: BlocBuilder<CreateCommunityBloc, CreateCommunityState>(
|
child: Column(
|
||||||
builder: (context, state) {
|
mainAxisSize: MainAxisSize.min,
|
||||||
return Column(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
children: [
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
DefaultTextStyle(
|
||||||
children: [
|
style: Theme.of(context).textTheme.headlineMedium!,
|
||||||
DefaultTextStyle(
|
child: widget.title,
|
||||||
style: Theme.of(context).textTheme.headlineMedium!,
|
),
|
||||||
child: widget.title,
|
const SizedBox(height: 18),
|
||||||
),
|
CreateCommunityNameTextField(nameController: _nameController),
|
||||||
const SizedBox(height: 18),
|
_buildErrorMessage(),
|
||||||
CreateCommunityNameTextField(
|
const SizedBox(height: 24),
|
||||||
nameController: _nameController,
|
_buildActionButtons(context),
|
||||||
),
|
],
|
||||||
if (state case CreateCommunityFailure(:final message))
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 18),
|
|
||||||
child: SelectableText(
|
|
||||||
'* $message',
|
|
||||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
|
||||||
color: Theme.of(context).colorScheme.error,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
_buildActionButtons(context),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -132,13 +118,22 @@ class _CreateCommunityDialogWidgetState extends State<CreateCommunityDialogWidge
|
|||||||
|
|
||||||
void _onSubmit(BuildContext context) {
|
void _onSubmit(BuildContext context) {
|
||||||
if (_formKey.currentState?.validate() ?? false) {
|
if (_formKey.currentState?.validate() ?? false) {
|
||||||
context.read<CreateCommunityBloc>().add(
|
widget.onSubmit.call(_nameController.text.trim());
|
||||||
CreateCommunity(
|
|
||||||
CreateCommunityParam(
|
|
||||||
name: _nameController.text.trim(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildErrorMessage() {
|
||||||
|
return Visibility(
|
||||||
|
visible: widget.errorMessage != null,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsetsDirectional.symmetric(vertical: 18),
|
||||||
|
child: SelectableText(
|
||||||
|
'* ${widget.errorMessage}',
|
||||||
|
style: context.textTheme.bodyMedium?.copyWith(
|
||||||
|
color: context.theme.colorScheme.error,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
@ -7,6 +7,9 @@ import 'package:syncrow_web/pages/space_management_v2/modules/communities/data/s
|
|||||||
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/params/load_communities_param.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/params/load_communities_param.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/bloc/communities_bloc.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/bloc/communities_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/communities_tree_selection_bloc/communities_tree_selection_bloc.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/communities_tree_selection_bloc/communities_tree_selection_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/data/services/remote_space_details_service.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/data/services/unique_subspaces_decorator.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/bloc/space_details_bloc.dart';
|
||||||
import 'package:syncrow_web/services/api/http_service.dart';
|
import 'package:syncrow_web/services/api/http_service.dart';
|
||||||
import 'package:syncrow_web/utils/theme/responsive_text_theme.dart';
|
import 'package:syncrow_web/utils/theme/responsive_text_theme.dart';
|
||||||
import 'package:syncrow_web/web_layout/web_scaffold.dart';
|
import 'package:syncrow_web/web_layout/web_scaffold.dart';
|
||||||
@ -26,6 +29,13 @@ class SpaceManagementPage extends StatelessWidget {
|
|||||||
)..add(const LoadCommunities(LoadCommunitiesParam())),
|
)..add(const LoadCommunities(LoadCommunitiesParam())),
|
||||||
),
|
),
|
||||||
BlocProvider(create: (context) => CommunitiesTreeSelectionBloc()),
|
BlocProvider(create: (context) => CommunitiesTreeSelectionBloc()),
|
||||||
|
BlocProvider(
|
||||||
|
create: (context) => SpaceDetailsBloc(
|
||||||
|
UniqueSubspacesDecorator(
|
||||||
|
RemoteSpaceDetailsService(httpService: HTTPService()),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
child: WebScaffold(
|
child: WebScaffold(
|
||||||
appBarTitle: Text(
|
appBarTitle: Text(
|
||||||
|
@ -0,0 +1,109 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/main_module/shared/helpers/space_management_community_dialog_helper.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/community_structure_header_action_buttons.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/communities_tree_selection_bloc/communities_tree_selection_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/helpers/space_details_dialog_helper.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
|
||||||
|
class CommunityStructureHeader extends StatelessWidget {
|
||||||
|
const CommunityStructureHeader({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
final screenWidth = MediaQuery.of(context).size.width;
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: ColorsManager.whiteColors,
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: ColorsManager.shadowBlackColor,
|
||||||
|
blurRadius: 8,
|
||||||
|
offset: const Offset(0, 4),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: _buildCommunityInfo(context, theme, screenWidth),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildCommunityInfo(
|
||||||
|
BuildContext context, ThemeData theme, double screenWidth) {
|
||||||
|
final selectedCommunity =
|
||||||
|
context.watch<CommunitiesTreeSelectionBloc>().state.selectedCommunity;
|
||||||
|
final selectedSpace =
|
||||||
|
context.watch<CommunitiesTreeSelectionBloc>().state.selectedSpace;
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Community Structure',
|
||||||
|
style: theme.textTheme.headlineLarge
|
||||||
|
?.copyWith(color: ColorsManager.blackColor),
|
||||||
|
),
|
||||||
|
if (selectedCommunity != null)
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: SelectableText(
|
||||||
|
selectedCommunity.name,
|
||||||
|
style: theme.textTheme.bodyLarge
|
||||||
|
?.copyWith(color: ColorsManager.blackColor),
|
||||||
|
maxLines: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 2),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
SpaceManagementCommunityDialogHelper.showEditDialog(
|
||||||
|
context,
|
||||||
|
selectedCommunity,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
Assets.iconEdit,
|
||||||
|
width: 16,
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
CommunityStructureHeaderActionButtons(
|
||||||
|
onDelete: (space) {},
|
||||||
|
onDuplicate: (space) {},
|
||||||
|
onEdit: (space) {
|
||||||
|
SpaceDetailsDialogHelper.showEdit(
|
||||||
|
context,
|
||||||
|
spaceModel: selectedSpace!,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
selectedSpace: selectedSpace,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/community_structure_header_button.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/space_model.dart';
|
||||||
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
|
||||||
|
class CommunityStructureHeaderActionButtons extends StatelessWidget {
|
||||||
|
const CommunityStructureHeaderActionButtons({
|
||||||
|
super.key,
|
||||||
|
required this.onDelete,
|
||||||
|
required this.selectedSpace,
|
||||||
|
required this.onDuplicate,
|
||||||
|
required this.onEdit,
|
||||||
|
});
|
||||||
|
|
||||||
|
final void Function(SpaceModel space) onDelete;
|
||||||
|
final void Function(SpaceModel space) onDuplicate;
|
||||||
|
final void Function(SpaceModel space) onEdit;
|
||||||
|
final SpaceModel? selectedSpace;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Wrap(
|
||||||
|
alignment: WrapAlignment.end,
|
||||||
|
spacing: 10,
|
||||||
|
children: [
|
||||||
|
if (selectedSpace != null) ...[
|
||||||
|
CommunityStructureHeaderButton(
|
||||||
|
label: 'Edit',
|
||||||
|
svgAsset: Assets.editSpace,
|
||||||
|
onPressed: () => onEdit(selectedSpace!),
|
||||||
|
),
|
||||||
|
CommunityStructureHeaderButton(
|
||||||
|
label: 'Duplicate',
|
||||||
|
svgAsset: Assets.duplicate,
|
||||||
|
onPressed: () => onDuplicate(selectedSpace!),
|
||||||
|
),
|
||||||
|
CommunityStructureHeaderButton(
|
||||||
|
label: 'Delete',
|
||||||
|
svgAsset: Assets.spaceDelete,
|
||||||
|
onPressed: () => onDelete(selectedSpace!),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
|
class CommunityStructureHeaderButton extends StatelessWidget {
|
||||||
|
const CommunityStructureHeaderButton({
|
||||||
|
super.key,
|
||||||
|
required this.label,
|
||||||
|
required this.onPressed,
|
||||||
|
this.svgAsset,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String label;
|
||||||
|
final VoidCallback onPressed;
|
||||||
|
final String? svgAsset;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
const double buttonHeight = 40;
|
||||||
|
return ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
maxWidth: 130,
|
||||||
|
minHeight: buttonHeight,
|
||||||
|
),
|
||||||
|
child: DefaultButton(
|
||||||
|
onPressed: onPressed,
|
||||||
|
borderWidth: 2,
|
||||||
|
backgroundColor: ColorsManager.textFieldGreyColor,
|
||||||
|
foregroundColor: ColorsManager.blackColor,
|
||||||
|
borderRadius: 12.0,
|
||||||
|
padding: 2.0,
|
||||||
|
height: buttonHeight,
|
||||||
|
elevation: 0,
|
||||||
|
borderColor: ColorsManager.lightGrayColor,
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
if (svgAsset != null)
|
||||||
|
SvgPicture.asset(
|
||||||
|
svgAsset!,
|
||||||
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Flexible(
|
||||||
|
child: Text(
|
||||||
|
label,
|
||||||
|
style: context.textTheme.bodySmall
|
||||||
|
?.copyWith(color: ColorsManager.blackColor, fontSize: 14),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
maxLines: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/community_structure_canvas.dart';
|
import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/community_structure_canvas.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/community_structure_header.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/create_space_button.dart';
|
import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/create_space_button.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/communities_tree_selection_bloc/communities_tree_selection_bloc.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/communities_tree_selection_bloc/communities_tree_selection_bloc.dart';
|
||||||
|
|
||||||
@ -18,9 +19,17 @@ class SpaceManagementCommunityStructure extends StatelessWidget {
|
|||||||
replacement: const Row(
|
replacement: const Row(
|
||||||
children: [spacer, Expanded(child: CreateSpaceButton()), spacer],
|
children: [spacer, Expanded(child: CreateSpaceButton()), spacer],
|
||||||
),
|
),
|
||||||
child: CommunityStructureCanvas(
|
child: Column(
|
||||||
community: selectedCommunity,
|
mainAxisSize: MainAxisSize.min,
|
||||||
selectedSpace: selectedSpace,
|
children: [
|
||||||
|
const CommunityStructureHeader(),
|
||||||
|
Expanded(
|
||||||
|
child: CommunityStructureCanvas(
|
||||||
|
community: selectedCommunity,
|
||||||
|
selectedSpace: selectedSpace,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,26 @@ class CommunityModel extends Equatable {
|
|||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CommunityModel copyWith({
|
||||||
|
String? uuid,
|
||||||
|
String? name,
|
||||||
|
DateTime? createdAt,
|
||||||
|
DateTime? updatedAt,
|
||||||
|
String? description,
|
||||||
|
String? externalId,
|
||||||
|
List<SpaceModel>? spaces,
|
||||||
|
}) {
|
||||||
|
return CommunityModel(
|
||||||
|
uuid: uuid ?? this.uuid,
|
||||||
|
name: name ?? this.name,
|
||||||
|
createdAt: createdAt ?? this.createdAt,
|
||||||
|
updatedAt: updatedAt ?? this.updatedAt,
|
||||||
|
description: description ?? this.description,
|
||||||
|
externalId: externalId ?? this.externalId,
|
||||||
|
spaces: spaces ?? this.spaces,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [uuid, name, spaces];
|
List<Object?> get props => [uuid, name, spaces];
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,16 @@ class SpaceModel extends Equatable {
|
|||||||
required this.parent,
|
required this.parent,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
factory SpaceModel.empty() => const SpaceModel(
|
||||||
|
uuid: '',
|
||||||
|
createdAt: null,
|
||||||
|
updatedAt: null,
|
||||||
|
spaceName: '',
|
||||||
|
icon: '',
|
||||||
|
children: [],
|
||||||
|
parent: null,
|
||||||
|
);
|
||||||
|
|
||||||
factory SpaceModel.fromJson(Map<String, dynamic> json) {
|
factory SpaceModel.fromJson(Map<String, dynamic> json) {
|
||||||
return SpaceModel(
|
return SpaceModel(
|
||||||
uuid: json['uuid'] as String? ?? '',
|
uuid: json['uuid'] as String? ?? '',
|
||||||
|
@ -16,6 +16,7 @@ class CommunitiesBloc extends Bloc<CommunitiesEvent, CommunitiesState> {
|
|||||||
on<LoadCommunities>(_onLoadCommunities);
|
on<LoadCommunities>(_onLoadCommunities);
|
||||||
on<LoadMoreCommunities>(_onLoadMoreCommunities);
|
on<LoadMoreCommunities>(_onLoadMoreCommunities);
|
||||||
on<InsertCommunity>(_onInsertCommunity);
|
on<InsertCommunity>(_onInsertCommunity);
|
||||||
|
on<CommunitiesUpdateCommunity>(_onCommunitiesUpdateCommunity);
|
||||||
}
|
}
|
||||||
|
|
||||||
final CommunitiesService _communitiesService;
|
final CommunitiesService _communitiesService;
|
||||||
@ -114,4 +115,18 @@ class CommunitiesBloc extends Bloc<CommunitiesEvent, CommunitiesState> {
|
|||||||
) {
|
) {
|
||||||
emit(state.copyWith(communities: [event.community, ...state.communities]));
|
emit(state.copyWith(communities: [event.community, ...state.communities]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _onCommunitiesUpdateCommunity(
|
||||||
|
CommunitiesUpdateCommunity event,
|
||||||
|
Emitter<CommunitiesState> emit,
|
||||||
|
) {
|
||||||
|
final updatedCommunities = state.communities
|
||||||
|
.map((e) => e.uuid == event.community.uuid ? event.community : e)
|
||||||
|
.toList();
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
communities: updatedCommunities,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,3 +31,12 @@ final class InsertCommunity extends CommunitiesEvent {
|
|||||||
@override
|
@override
|
||||||
List<Object?> get props => [community];
|
List<Object?> get props => [community];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final class CommunitiesUpdateCommunity extends CommunitiesEvent {
|
||||||
|
const CommunitiesUpdateCommunity(this.community);
|
||||||
|
|
||||||
|
final CommunityModel community;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [community];
|
||||||
|
}
|
||||||
|
@ -1,57 +1,58 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart';
|
import 'package:syncrow_web/pages/space_management_v2/main_module/shared/helpers/space_management_community_dialog_helper.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/main_module/shared/widgets/community_dialog.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/bloc/communities_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/communities_tree_selection_bloc/communities_tree_selection_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/create_community/data/services/remote_create_community_service.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/create_community/data/services/remote_create_community_service.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/create_community/domain/param/create_community_param.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/create_community/presentation/bloc/create_community_bloc.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/create_community/presentation/bloc/create_community_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/create_community/presentation/create_community_dialog_widget.dart';
|
|
||||||
import 'package:syncrow_web/services/api/http_service.dart';
|
import 'package:syncrow_web/services/api/http_service.dart';
|
||||||
|
|
||||||
class CreateCommunityDialog extends StatelessWidget {
|
class CreateCommunityDialog extends StatelessWidget {
|
||||||
final void Function(CommunityModel community) onCreateCommunity;
|
const CreateCommunityDialog({super.key});
|
||||||
final String? initialName;
|
|
||||||
final Widget title;
|
|
||||||
|
|
||||||
const CreateCommunityDialog({
|
|
||||||
super.key,
|
|
||||||
required this.onCreateCommunity,
|
|
||||||
required this.title,
|
|
||||||
this.initialName,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (_) => CreateCommunityBloc(RemoteCreateCommunityService(HTTPService())),
|
create: (_) => CreateCommunityBloc(
|
||||||
child: BlocListener<CreateCommunityBloc, CreateCommunityState>(
|
RemoteCreateCommunityService(HTTPService()),
|
||||||
|
),
|
||||||
|
child: BlocConsumer<CreateCommunityBloc, CreateCommunityState>(
|
||||||
listener: (context, state) {
|
listener: (context, state) {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case CreateCommunityLoading():
|
case CreateCommunityLoading() || CreateCommunityInitial():
|
||||||
showDialog<void>(
|
SpaceManagementCommunityDialogHelper.showLoadingDialog(context);
|
||||||
context: context,
|
|
||||||
builder: (context) => const Center(
|
|
||||||
child: CircularProgressIndicator(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
case CreateCommunitySuccess(:final community):
|
case CreateCommunitySuccess(:final community):
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
SpaceManagementCommunityDialogHelper.showSuccessSnackBar(
|
||||||
const SnackBar(content: Text('Community created successfully')),
|
context,
|
||||||
|
'${community.name} community created successfully',
|
||||||
);
|
);
|
||||||
onCreateCommunity.call(community);
|
context.read<CommunitiesBloc>().add(
|
||||||
|
InsertCommunity(community),
|
||||||
|
);
|
||||||
|
context.read<CommunitiesTreeSelectionBloc>().add(
|
||||||
|
SelectCommunityEvent(community: community),
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case CreateCommunityFailure():
|
case CreateCommunityFailure():
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: CreateCommunityDialogWidget(
|
builder: (BuildContext context, CreateCommunityState state) {
|
||||||
title: title,
|
return CommunityDialog(
|
||||||
initialName: initialName,
|
title: const Text('Create Community'),
|
||||||
),
|
initialName: null,
|
||||||
|
onSubmit: (name) => context.read<CreateCommunityBloc>().add(
|
||||||
|
CreateCommunity(CreateCommunityParam(name: name)),
|
||||||
|
),
|
||||||
|
errorMessage: state is CreateCommunityFailure ? state.message : null,
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/params/load_spaces_param.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/params/load_space_details_param.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/services/space_details_service.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/services/space_details_service.dart';
|
||||||
import 'package:syncrow_web/services/api/api_exception.dart';
|
import 'package:syncrow_web/services/api/api_exception.dart';
|
||||||
import 'package:syncrow_web/services/api/http_service.dart';
|
import 'package:syncrow_web/services/api/http_service.dart';
|
||||||
@ -15,12 +16,15 @@ class RemoteSpaceDetailsService implements SpaceDetailsService {
|
|||||||
static const _defaultErrorMessage = 'Failed to load space details';
|
static const _defaultErrorMessage = 'Failed to load space details';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<SpaceDetailsModel> getSpaceDetails(LoadSpacesParam param) async {
|
Future<SpaceDetailsModel> getSpaceDetails(LoadSpaceDetailsParam param) async {
|
||||||
try {
|
try {
|
||||||
final response = await _httpService.get(
|
final response = await _httpService.get(
|
||||||
path: 'endpoint',
|
path: await _makeEndpoint(param),
|
||||||
expectedResponseModel: (data) {
|
expectedResponseModel: (data) {
|
||||||
return SpaceDetailsModel.fromJson(data as Map<String, dynamic>);
|
final response = data as Map<String, dynamic>;
|
||||||
|
return SpaceDetailsModel.fromJson(
|
||||||
|
response['data'] as Map<String, dynamic>,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return response;
|
return response;
|
||||||
@ -37,4 +41,13 @@ class RemoteSpaceDetailsService implements SpaceDetailsService {
|
|||||||
throw APIException(formattedErrorMessage);
|
throw APIException(formattedErrorMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<String> _makeEndpoint(LoadSpaceDetailsParam param) async {
|
||||||
|
final projectUuid = await ProjectManager.getProjectUUID();
|
||||||
|
if (projectUuid == null || projectUuid.isEmpty) {
|
||||||
|
throw APIException('Project UUID is not set');
|
||||||
|
}
|
||||||
|
|
||||||
|
return '/projects/$projectUuid/communities/${param.communityUuid}/spaces/${param.spaceUuid}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/params/load_space_details_param.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/services/space_details_service.dart';
|
||||||
|
|
||||||
|
class UniqueSubspacesDecorator implements SpaceDetailsService {
|
||||||
|
final SpaceDetailsService _decoratee;
|
||||||
|
|
||||||
|
const UniqueSubspacesDecorator(this._decoratee);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<SpaceDetailsModel> getSpaceDetails(LoadSpaceDetailsParam param) async {
|
||||||
|
final response = await _decoratee.getSpaceDetails(param);
|
||||||
|
|
||||||
|
final uniqueSubspaces = <String, Subspace>{};
|
||||||
|
|
||||||
|
for (final subspace in response.subspaces) {
|
||||||
|
final normalizedName = subspace.name.trim().toLowerCase();
|
||||||
|
if (!uniqueSubspaces.containsKey(normalizedName)) {
|
||||||
|
uniqueSubspaces[normalizedName] = subspace;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.copyWith(
|
||||||
|
subspaces: uniqueSubspaces.values.toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/products/domain/models/product.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/products/domain/models/product.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/tags/domain/models/tag.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/tags/domain/models/tag.dart';
|
||||||
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
|
||||||
class SpaceDetailsModel extends Equatable {
|
class SpaceDetailsModel extends Equatable {
|
||||||
final String uuid;
|
final String uuid;
|
||||||
@ -17,14 +18,21 @@ class SpaceDetailsModel extends Equatable {
|
|||||||
required this.subspaces,
|
required this.subspaces,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
factory SpaceDetailsModel.empty() => const SpaceDetailsModel(
|
||||||
|
uuid: '',
|
||||||
|
spaceName: '',
|
||||||
|
icon: Assets.villa,
|
||||||
|
productAllocations: [],
|
||||||
|
subspaces: [],
|
||||||
|
);
|
||||||
factory SpaceDetailsModel.fromJson(Map<String, dynamic> json) {
|
factory SpaceDetailsModel.fromJson(Map<String, dynamic> json) {
|
||||||
return SpaceDetailsModel(
|
return SpaceDetailsModel(
|
||||||
uuid: json['uuid'] as String,
|
uuid: json['uuid'] as String,
|
||||||
spaceName: json['spaceName'] as String,
|
spaceName: json['spaceName'] as String,
|
||||||
icon: json['icon'] as String,
|
icon: json['icon'] as String,
|
||||||
productAllocations: (json['productAllocations'] as List)
|
productAllocations: (json['productAllocations'] as List)
|
||||||
.map((e) => ProductAllocation.fromJson(e as Map<String, dynamic>))
|
.map((e) => ProductAllocation.fromJson(e as Map<String, dynamic>))
|
||||||
.toList(),
|
.toList(),
|
||||||
subspaces: (json['subspaces'] as List)
|
subspaces: (json['subspaces'] as List)
|
||||||
.map((e) => Subspace.fromJson(e as Map<String, dynamic>))
|
.map((e) => Subspace.fromJson(e as Map<String, dynamic>))
|
||||||
.toList(),
|
.toList(),
|
||||||
@ -41,6 +49,22 @@ class SpaceDetailsModel extends Equatable {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SpaceDetailsModel copyWith({
|
||||||
|
String? uuid,
|
||||||
|
String? spaceName,
|
||||||
|
String? icon,
|
||||||
|
List<ProductAllocation>? productAllocations,
|
||||||
|
List<Subspace>? subspaces,
|
||||||
|
}) {
|
||||||
|
return SpaceDetailsModel(
|
||||||
|
uuid: uuid ?? this.uuid,
|
||||||
|
spaceName: spaceName ?? this.spaceName,
|
||||||
|
icon: icon ?? this.icon,
|
||||||
|
productAllocations: productAllocations ?? this.productAllocations,
|
||||||
|
subspaces: subspaces ?? this.subspaces,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [uuid, spaceName, icon, productAllocations, subspaces];
|
List<Object?> get props => [uuid, spaceName, icon, productAllocations, subspaces];
|
||||||
}
|
}
|
||||||
@ -48,12 +72,10 @@ class SpaceDetailsModel extends Equatable {
|
|||||||
class ProductAllocation extends Equatable {
|
class ProductAllocation extends Equatable {
|
||||||
final Product product;
|
final Product product;
|
||||||
final Tag tag;
|
final Tag tag;
|
||||||
final String? location;
|
|
||||||
|
|
||||||
const ProductAllocation({
|
const ProductAllocation({
|
||||||
required this.product,
|
required this.product,
|
||||||
required this.tag,
|
required this.tag,
|
||||||
this.location,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
factory ProductAllocation.fromJson(Map<String, dynamic> json) {
|
factory ProductAllocation.fromJson(Map<String, dynamic> json) {
|
||||||
@ -70,6 +92,16 @@ class ProductAllocation extends Equatable {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ProductAllocation copyWith({
|
||||||
|
Product? product,
|
||||||
|
Tag? tag,
|
||||||
|
}) {
|
||||||
|
return ProductAllocation(
|
||||||
|
product: product ?? this.product,
|
||||||
|
tag: tag ?? this.tag,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [product, tag];
|
List<Object?> get props => [product, tag];
|
||||||
}
|
}
|
||||||
@ -88,7 +120,7 @@ class Subspace extends Equatable {
|
|||||||
factory Subspace.fromJson(Map<String, dynamic> json) {
|
factory Subspace.fromJson(Map<String, dynamic> json) {
|
||||||
return Subspace(
|
return Subspace(
|
||||||
uuid: json['uuid'] as String,
|
uuid: json['uuid'] as String,
|
||||||
name: json['name'] as String,
|
name: json['subspaceName'] as String,
|
||||||
productAllocations: (json['productAllocations'] as List)
|
productAllocations: (json['productAllocations'] as List)
|
||||||
.map((e) => ProductAllocation.fromJson(e as Map<String, dynamic>))
|
.map((e) => ProductAllocation.fromJson(e as Map<String, dynamic>))
|
||||||
.toList(),
|
.toList(),
|
||||||
@ -103,6 +135,18 @@ class Subspace extends Equatable {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Subspace copyWith({
|
||||||
|
String? uuid,
|
||||||
|
String? name,
|
||||||
|
List<ProductAllocation>? productAllocations,
|
||||||
|
}) {
|
||||||
|
return Subspace(
|
||||||
|
uuid: uuid ?? this.uuid,
|
||||||
|
name: name ?? this.name,
|
||||||
|
productAllocations: productAllocations ?? this.productAllocations,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [uuid, name, productAllocations];
|
List<Object?> get props => [uuid, name, productAllocations];
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
class LoadSpaceDetailsParam {
|
||||||
|
const LoadSpaceDetailsParam({
|
||||||
|
required this.spaceUuid,
|
||||||
|
required this.communityUuid,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String spaceUuid;
|
||||||
|
final String communityUuid;
|
||||||
|
}
|
@ -1,3 +0,0 @@
|
|||||||
class LoadSpacesParam {
|
|
||||||
const LoadSpacesParam();
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/params/load_spaces_param.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/params/load_space_details_param.dart';
|
||||||
|
|
||||||
abstract class SpaceDetailsService {
|
abstract class SpaceDetailsService {
|
||||||
Future<SpaceDetailsModel> getSpaceDetails(LoadSpacesParam param);
|
Future<SpaceDetailsModel> getSpaceDetails(LoadSpaceDetailsParam param);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/params/load_spaces_param.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/params/load_space_details_param.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/services/space_details_service.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/services/space_details_service.dart';
|
||||||
import 'package:syncrow_web/services/api/api_exception.dart';
|
import 'package:syncrow_web/services/api/api_exception.dart';
|
||||||
|
|
||||||
@ -9,12 +9,13 @@ part 'space_details_event.dart';
|
|||||||
part 'space_details_state.dart';
|
part 'space_details_state.dart';
|
||||||
|
|
||||||
class SpaceDetailsBloc extends Bloc<SpaceDetailsEvent, SpaceDetailsState> {
|
class SpaceDetailsBloc extends Bloc<SpaceDetailsEvent, SpaceDetailsState> {
|
||||||
final SpaceDetailsService _spaceDetailsService;
|
|
||||||
|
|
||||||
SpaceDetailsBloc(this._spaceDetailsService) : super(SpaceDetailsInitial()) {
|
SpaceDetailsBloc(this._spaceDetailsService) : super(SpaceDetailsInitial()) {
|
||||||
on<LoadSpaceDetails>(_onLoadSpaceDetails);
|
on<LoadSpaceDetails>(_onLoadSpaceDetails);
|
||||||
|
on<ClearSpaceDetails>(_onClearSpaceDetails);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final SpaceDetailsService _spaceDetailsService;
|
||||||
|
|
||||||
Future<void> _onLoadSpaceDetails(
|
Future<void> _onLoadSpaceDetails(
|
||||||
LoadSpaceDetails event,
|
LoadSpaceDetails event,
|
||||||
Emitter<SpaceDetailsState> emit,
|
Emitter<SpaceDetailsState> emit,
|
||||||
@ -31,4 +32,11 @@ class SpaceDetailsBloc extends Bloc<SpaceDetailsEvent, SpaceDetailsState> {
|
|||||||
emit(SpaceDetailsFailure(e.toString()));
|
emit(SpaceDetailsFailure(e.toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _onClearSpaceDetails(
|
||||||
|
ClearSpaceDetails event,
|
||||||
|
Emitter<SpaceDetailsState> emit,
|
||||||
|
) {
|
||||||
|
emit(SpaceDetailsInitial());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,18 @@ sealed class SpaceDetailsEvent extends Equatable {
|
|||||||
List<Object> get props => [];
|
List<Object> get props => [];
|
||||||
}
|
}
|
||||||
|
|
||||||
class LoadSpaceDetails extends SpaceDetailsEvent {
|
final class LoadSpaceDetails extends SpaceDetailsEvent {
|
||||||
const LoadSpaceDetails(this.param);
|
const LoadSpaceDetails(this.param);
|
||||||
|
|
||||||
final LoadSpacesParam param;
|
final LoadSpaceDetailsParam param;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [param];
|
List<Object> get props => [param];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final class ClearSpaceDetails extends SpaceDetailsEvent {
|
||||||
|
const ClearSpaceDetails();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [];
|
||||||
|
}
|
@ -21,10 +21,10 @@ final class SpaceDetailsLoaded extends SpaceDetailsState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final class SpaceDetailsFailure extends SpaceDetailsState {
|
final class SpaceDetailsFailure extends SpaceDetailsState {
|
||||||
final String message;
|
final String errorMessage;
|
||||||
|
|
||||||
const SpaceDetailsFailure(this.message);
|
const SpaceDetailsFailure(this.errorMessage);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [message];
|
List<Object> get props => [errorMessage];
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,46 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/space_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/data/services/remote_space_details_service.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/bloc/space_details_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_dialog.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_dialog.dart';
|
||||||
|
import 'package:syncrow_web/services/api/http_service.dart';
|
||||||
|
|
||||||
abstract final class SpaceDetailsDialogHelper {
|
abstract final class SpaceDetailsDialogHelper {
|
||||||
static void showCreate(BuildContext context) {
|
static void showCreate(BuildContext context) {
|
||||||
showDialog<void>(
|
showDialog<void>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => const SpaceDetailsDialog(),
|
builder: (_) => BlocProvider(
|
||||||
|
create: (context) => SpaceDetailsBloc(
|
||||||
|
RemoteSpaceDetailsService(httpService: HTTPService()),
|
||||||
|
),
|
||||||
|
child: SpaceDetailsDialog(
|
||||||
|
context: context,
|
||||||
|
title: const SelectableText('Create Space'),
|
||||||
|
spaceModel: SpaceModel.empty(),
|
||||||
|
onSave: print,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void showEdit(
|
||||||
|
BuildContext context, {
|
||||||
|
required SpaceModel spaceModel,
|
||||||
|
}) {
|
||||||
|
showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (_) => BlocProvider(
|
||||||
|
create: (context) => SpaceDetailsBloc(
|
||||||
|
RemoteSpaceDetailsService(httpService: HTTPService()),
|
||||||
|
),
|
||||||
|
child: SpaceDetailsDialog(
|
||||||
|
context: context,
|
||||||
|
title: const SelectableText('Edit Space'),
|
||||||
|
spaceModel: spaceModel,
|
||||||
|
onSave: (space) {},
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,60 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
|
class ButtonContentWidget extends StatelessWidget {
|
||||||
|
final String label;
|
||||||
|
final String? svgAssets;
|
||||||
|
final bool disabled;
|
||||||
|
|
||||||
|
const ButtonContentWidget({
|
||||||
|
required this.label,
|
||||||
|
this.svgAssets,
|
||||||
|
this.disabled = false,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final screenWidth = MediaQuery.of(context).size.width;
|
||||||
|
|
||||||
|
return Opacity(
|
||||||
|
opacity: disabled ? 0.5 : 1.0,
|
||||||
|
child: Container(
|
||||||
|
width: screenWidth * 0.25,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: ColorsManager.textFieldGreyColor,
|
||||||
|
border: Border.all(
|
||||||
|
color: ColorsManager.neutralGray,
|
||||||
|
width: 3.0,
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 16.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
if (svgAssets != null)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 6.0),
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
svgAssets!,
|
||||||
|
width: screenWidth * 0.015,
|
||||||
|
height: screenWidth * 0.015,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
label,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: ColorsManager.blackColor,
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
|
||||||
|
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
|
class SpaceDetailsActionButtons extends StatelessWidget {
|
||||||
|
const SpaceDetailsActionButtons({
|
||||||
|
super.key,
|
||||||
|
required this.onSave,
|
||||||
|
required this.onCancel,
|
||||||
|
});
|
||||||
|
|
||||||
|
final VoidCallback onCancel;
|
||||||
|
final VoidCallback? onSave;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
spacing: 10,
|
||||||
|
children: [
|
||||||
|
Expanded(child: _buildCancelButton(context)),
|
||||||
|
Expanded(child: _buildSaveButton()),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildCancelButton(BuildContext context) {
|
||||||
|
return CancelButton(
|
||||||
|
onPressed: onCancel,
|
||||||
|
label: 'Cancel',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildSaveButton() {
|
||||||
|
return DefaultButton(
|
||||||
|
onPressed: onSave,
|
||||||
|
borderRadius: 10,
|
||||||
|
backgroundColor: ColorsManager.secondaryColor,
|
||||||
|
foregroundColor: ColorsManager.whiteColors,
|
||||||
|
child: const Text('OK'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,92 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/common/edit_chip.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/button_content_widget.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
|
||||||
|
class SpaceDetailsDevicesBox extends StatelessWidget {
|
||||||
|
const SpaceDetailsDevicesBox({
|
||||||
|
required this.space,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final SpaceDetailsModel space;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final productAllocations = space.productAllocations;
|
||||||
|
final subspaces = space.subspaces;
|
||||||
|
final isAnySubspaceHasProductAllocations =
|
||||||
|
subspaces.any((subspace) => subspace.productAllocations.isNotEmpty);
|
||||||
|
if (productAllocations.isNotEmpty || isAnySubspaceHasProductAllocations) {
|
||||||
|
return Container(
|
||||||
|
width: double.infinity,
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: ColorsManager.textFieldGreyColor,
|
||||||
|
borderRadius: BorderRadius.circular(15),
|
||||||
|
border: Border.all(
|
||||||
|
color: ColorsManager.textFieldGreyColor,
|
||||||
|
width: 3.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Wrap(
|
||||||
|
spacing: 8.0,
|
||||||
|
runSpacing: 8.0,
|
||||||
|
children: [
|
||||||
|
// Combine tags from spaceModel and subspaces
|
||||||
|
// ...TagHelper.groupTags([
|
||||||
|
// ...?tags,
|
||||||
|
// ...?subspaces?.expand((subspace) => subspace.tags ?? [])
|
||||||
|
// ]).entries.map(
|
||||||
|
// (entry) => Chip(
|
||||||
|
// avatar: SizedBox(
|
||||||
|
// width: 24,
|
||||||
|
// height: 24,
|
||||||
|
// child: SvgPicture.asset(
|
||||||
|
// entry.key.icon ?? 'assets/icons/gateway.svg',
|
||||||
|
// fit: BoxFit.contain,
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// label: Text(
|
||||||
|
// 'x${entry.value}',
|
||||||
|
// style: Theme.of(context)
|
||||||
|
// .textTheme
|
||||||
|
// .bodySmall
|
||||||
|
// ?.copyWith(color: ColorsManager.spaceColor),
|
||||||
|
// ),
|
||||||
|
// backgroundColor: ColorsManager.whiteColors,
|
||||||
|
// shape: RoundedRectangleBorder(
|
||||||
|
// borderRadius: BorderRadius.circular(16),
|
||||||
|
// side: const BorderSide(
|
||||||
|
// color: ColorsManager.spaceColor,
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
|
||||||
|
EditChip(
|
||||||
|
onTap: () {},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return TextButton(
|
||||||
|
onPressed: () {},
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
),
|
||||||
|
child: const SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
child: ButtonContentWidget(
|
||||||
|
svgAssets: Assets.addIcon,
|
||||||
|
label: 'Add Devices',
|
||||||
|
// disabled: isTagsAndSubspaceModelDisabled,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,105 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/space_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/communities_tree_selection_bloc/communities_tree_selection_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/params/load_space_details_param.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/bloc/space_details_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_form.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
class SpaceDetailsDialog extends StatelessWidget {
|
class SpaceDetailsDialog extends StatefulWidget {
|
||||||
const SpaceDetailsDialog({super.key});
|
const SpaceDetailsDialog({
|
||||||
|
required this.title,
|
||||||
|
required this.spaceModel,
|
||||||
|
required this.onSave,
|
||||||
|
required this.context,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Widget title;
|
||||||
|
final SpaceModel spaceModel;
|
||||||
|
final void Function(SpaceDetailsModel space) onSave;
|
||||||
|
final BuildContext context;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SpaceDetailsDialog> createState() => _SpaceDetailsDialogState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SpaceDetailsDialogState extends State<SpaceDetailsDialog> {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
final isCreateMode = widget.spaceModel.uuid.isEmpty;
|
||||||
|
|
||||||
|
if (!isCreateMode) {
|
||||||
|
final param = LoadSpaceDetailsParam(
|
||||||
|
spaceUuid: widget.spaceModel.uuid,
|
||||||
|
communityUuid: widget.context
|
||||||
|
.read<CommunitiesTreeSelectionBloc>()
|
||||||
|
.state
|
||||||
|
.selectedCommunity!
|
||||||
|
.uuid,
|
||||||
|
);
|
||||||
|
widget.context.read<SpaceDetailsBloc>().add(LoadSpaceDetails(param));
|
||||||
|
}
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return const Dialog(
|
final isCreateMode = widget.spaceModel.uuid.isEmpty;
|
||||||
child: Text('Create Space'),
|
if (isCreateMode) {
|
||||||
|
return SpaceDetailsForm(
|
||||||
|
title: widget.title,
|
||||||
|
space: SpaceDetailsModel.empty(),
|
||||||
|
onSave: widget.onSave,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return BlocBuilder<SpaceDetailsBloc, SpaceDetailsState>(
|
||||||
|
bloc: widget.context.read<SpaceDetailsBloc>(),
|
||||||
|
builder: (context, state) => switch (state) {
|
||||||
|
SpaceDetailsInitial() => _buildLoadingDialog(),
|
||||||
|
SpaceDetailsLoading() => _buildLoadingDialog(),
|
||||||
|
SpaceDetailsLoaded(:final spaceDetails) => SpaceDetailsForm(
|
||||||
|
title: widget.title,
|
||||||
|
space: spaceDetails,
|
||||||
|
onSave: widget.onSave,
|
||||||
|
),
|
||||||
|
SpaceDetailsFailure(:final errorMessage) => _buildErrorDialog(
|
||||||
|
errorMessage,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildLoadingDialog() {
|
||||||
|
return AlertDialog(
|
||||||
|
title: widget.title,
|
||||||
|
backgroundColor: ColorsManager.whiteColors,
|
||||||
|
content: SizedBox(
|
||||||
|
height: context.screenHeight * 0.3,
|
||||||
|
width: context.screenWidth * 0.5,
|
||||||
|
child: const Center(child: CircularProgressIndicator()),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildErrorDialog(String errorMessage) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: widget.title,
|
||||||
|
backgroundColor: ColorsManager.whiteColors,
|
||||||
|
content: Center(
|
||||||
|
child: SelectableText(
|
||||||
|
errorMessage,
|
||||||
|
style: context.textTheme.bodyLarge?.copyWith(
|
||||||
|
color: ColorsManager.red,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
fontSize: 18,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,77 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_action_buttons.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_devices_box.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_icon_picker.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_name_text_field.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_sub_spaces_box.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/update_space/presentation/bloc/space_details_model_bloc/space_details_model_bloc.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
|
class SpaceDetailsForm extends StatelessWidget {
|
||||||
|
const SpaceDetailsForm({
|
||||||
|
required this.title,
|
||||||
|
required this.space,
|
||||||
|
required this.onSave,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Widget title;
|
||||||
|
final SpaceDetailsModel space;
|
||||||
|
final void Function(SpaceDetailsModel space) onSave;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => SpaceDetailsModelBloc(initialState: space),
|
||||||
|
child: BlocBuilder<SpaceDetailsModelBloc, SpaceDetailsModel>(
|
||||||
|
buildWhen: (previous, current) => previous != current,
|
||||||
|
builder: (context, space) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: title,
|
||||||
|
backgroundColor: ColorsManager.whiteColors,
|
||||||
|
content: SizedBox(
|
||||||
|
height: context.screenHeight * 0.3,
|
||||||
|
width: context.screenWidth * 0.5,
|
||||||
|
child: Row(
|
||||||
|
spacing: 20,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Expanded(child: SpaceIconPicker(iconPath: space.icon)),
|
||||||
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SpaceNameTextField(
|
||||||
|
initialValue: space.spaceName,
|
||||||
|
isNameFieldExist: (value) => space.subspaces.any(
|
||||||
|
(subspace) => subspace.name == value,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
SpaceSubSpacesBox(
|
||||||
|
subspaces: space.subspaces,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
SpaceDetailsDevicesBox(space: space),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
SpaceDetailsActionButtons(
|
||||||
|
onSave: () => onSave(space),
|
||||||
|
onCancel: Navigator.of(context).pop,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_icon_selection_dialog.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/update_space/presentation/bloc/space_details_model_bloc/space_details_model_bloc.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
|
class SpaceIconPicker extends StatelessWidget {
|
||||||
|
const SpaceIconPicker({
|
||||||
|
required this.iconPath,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String iconPath;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Center(
|
||||||
|
child: Stack(
|
||||||
|
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: context.screenWidth * 0.175,
|
||||||
|
height: context.screenHeight * 0.175,
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
color: ColorsManager.boxColor,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.all(24),
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
iconPath,
|
||||||
|
width: context.screenWidth * 0.08,
|
||||||
|
height: context.screenHeight * 0.08,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned.directional(
|
||||||
|
top: 12,
|
||||||
|
start: context.screenHeight * 0.06,
|
||||||
|
textDirection: Directionality.of(context),
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
showDialog<String?>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => SpaceIconSelectionDialog(
|
||||||
|
selectedIcon: iconPath,
|
||||||
|
),
|
||||||
|
).then((value) {
|
||||||
|
if (value != null) {
|
||||||
|
if (context.mounted) {
|
||||||
|
context.read<SpaceDetailsModelBloc>().add(
|
||||||
|
UpdateSpaceDetailsIcon(value),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
Assets.iconEdit,
|
||||||
|
width: 16,
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
|
class SpaceIconSelectionDialog extends StatelessWidget {
|
||||||
|
const SpaceIconSelectionDialog({super.key, required this.selectedIcon});
|
||||||
|
final String selectedIcon;
|
||||||
|
|
||||||
|
static const List<String> _icons = [
|
||||||
|
Assets.location,
|
||||||
|
Assets.villa,
|
||||||
|
Assets.gym,
|
||||||
|
Assets.sauna,
|
||||||
|
Assets.bbq,
|
||||||
|
Assets.building,
|
||||||
|
Assets.desk,
|
||||||
|
Assets.door,
|
||||||
|
Assets.parking,
|
||||||
|
Assets.pool,
|
||||||
|
Assets.stair,
|
||||||
|
Assets.steamRoom,
|
||||||
|
Assets.street,
|
||||||
|
Assets.unit,
|
||||||
|
];
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: SelectableText(
|
||||||
|
'Space Icon',
|
||||||
|
style: context.textTheme.headlineMedium,
|
||||||
|
),
|
||||||
|
backgroundColor: ColorsManager.whiteColors,
|
||||||
|
content: Container(
|
||||||
|
width: context.screenWidth * 0.45,
|
||||||
|
height: context.screenHeight * 0.275,
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: ColorsManager.boxColor,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: GridView.builder(
|
||||||
|
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
crossAxisCount: 7,
|
||||||
|
crossAxisSpacing: 8,
|
||||||
|
mainAxisSpacing: 16,
|
||||||
|
),
|
||||||
|
itemCount: _icons.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final isSelected = selectedIcon == _icons[index];
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsetsDirectional.all(2),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
border: isSelected
|
||||||
|
? Border.all(color: ColorsManager.vividBlue, width: 2)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
child: IconButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(_icons[index]),
|
||||||
|
icon: SvgPicture.asset(
|
||||||
|
_icons[index],
|
||||||
|
width: context.screenWidth * 0.03,
|
||||||
|
height: context.screenHeight * 0.08,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/update_space/presentation/bloc/space_details_model_bloc/space_details_model_bloc.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
|
class SpaceNameTextField extends StatefulWidget {
|
||||||
|
const SpaceNameTextField({
|
||||||
|
required this.initialValue,
|
||||||
|
required this.isNameFieldExist,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String? initialValue;
|
||||||
|
final bool Function(String value) isNameFieldExist;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SpaceNameTextField> createState() => _SpaceNameTextFieldState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SpaceNameTextFieldState extends State<SpaceNameTextField> {
|
||||||
|
late final TextEditingController _controller;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
_controller = TextEditingController(text: widget.initialValue);
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_controller.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
final _formKey = GlobalKey<FormState>();
|
||||||
|
|
||||||
|
String? _validateName(String? value) {
|
||||||
|
if (value == null || value.isEmpty) {
|
||||||
|
return '*Space name should not be empty.';
|
||||||
|
}
|
||||||
|
if (widget.isNameFieldExist(value)) {
|
||||||
|
return '*Name already exists';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Form(
|
||||||
|
key: _formKey,
|
||||||
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||||
|
child: TextFormField(
|
||||||
|
controller: _controller,
|
||||||
|
onChanged: (value) => context.read<SpaceDetailsModelBloc>().add(
|
||||||
|
UpdateSpaceDetailsName(value),
|
||||||
|
),
|
||||||
|
validator: _validateName,
|
||||||
|
style: context.textTheme.bodyMedium,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: 'Please enter the name',
|
||||||
|
hintStyle: context.textTheme.bodyMedium!.copyWith(
|
||||||
|
color: ColorsManager.lightGrayColor,
|
||||||
|
),
|
||||||
|
filled: true,
|
||||||
|
fillColor: ColorsManager.boxColor,
|
||||||
|
enabledBorder: _buildBorder(context, ColorsManager.vividBlue),
|
||||||
|
focusedBorder: _buildBorder(context, ColorsManager.primaryColor),
|
||||||
|
errorBorder: _buildBorder(context, context.theme.colorScheme.error),
|
||||||
|
focusedErrorBorder: _buildBorder(context, context.theme.colorScheme.error),
|
||||||
|
errorStyle: context.textTheme.bodySmall?.copyWith(
|
||||||
|
color: context.theme.colorScheme.error,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
OutlineInputBorder _buildBorder(BuildContext context, [Color? color]) {
|
||||||
|
return OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
borderSide: BorderSide(width: 1, color: color ?? ColorsManager.boxColor),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/common/edit_chip.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/button_content_widget.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_sub_spaces_dialog.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/subspace_name_display_widget.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/update_space/presentation/bloc/space_details_model_bloc/space_details_model_bloc.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
|
||||||
|
class SpaceSubSpacesBox extends StatelessWidget {
|
||||||
|
const SpaceSubSpacesBox({super.key, required this.subspaces});
|
||||||
|
|
||||||
|
final List<Subspace> subspaces;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (subspaces.isEmpty) {
|
||||||
|
return TextButton(
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
overlayColor: ColorsManager.transparentColor,
|
||||||
|
),
|
||||||
|
onPressed: () => _showSubSpacesDialog(context),
|
||||||
|
child: const SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
child: ButtonContentWidget(
|
||||||
|
svgAssets: Assets.addIcon,
|
||||||
|
label: 'Create Sub Spaces',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
width: double.infinity,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: ColorsManager.textFieldGreyColor,
|
||||||
|
borderRadius: BorderRadius.circular(15),
|
||||||
|
border: Border.all(
|
||||||
|
color: ColorsManager.textFieldGreyColor,
|
||||||
|
width: 3.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Wrap(
|
||||||
|
spacing: 8.0,
|
||||||
|
runSpacing: 8.0,
|
||||||
|
children: [
|
||||||
|
...subspaces.map((e) => SubspaceNameDisplayWidget(subSpace: e)),
|
||||||
|
EditChip(
|
||||||
|
onTap: () => _showSubSpacesDialog(context),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showSubSpacesDialog(BuildContext context) {
|
||||||
|
showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: false,
|
||||||
|
builder: (_) => SpaceSubSpacesDialog(
|
||||||
|
subspaces: subspaces,
|
||||||
|
onSave: (subspaces) {
|
||||||
|
context.read<SpaceDetailsModelBloc>().add(
|
||||||
|
UpdateSpaceDetailsSubspaces(subspaces),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,90 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_action_buttons.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/sub_spaces_input.dart';
|
||||||
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
|
class SpaceSubSpacesDialog extends StatefulWidget {
|
||||||
|
const SpaceSubSpacesDialog({
|
||||||
|
required this.subspaces,
|
||||||
|
required this.onSave,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final List<Subspace> subspaces;
|
||||||
|
final void Function(List<Subspace> subspaces) onSave;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SpaceSubSpacesDialog> createState() => _SpaceSubSpacesDialogState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SpaceSubSpacesDialogState extends State<SpaceSubSpacesDialog> {
|
||||||
|
late List<Subspace> _subspaces;
|
||||||
|
|
||||||
|
bool get _hasDuplicateNames =>
|
||||||
|
_subspaces.map((subspace) => subspace.name.toLowerCase()).toSet().length !=
|
||||||
|
_subspaces.length;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_subspaces = List.from(widget.subspaces);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleSubspaceAdded(String name) {
|
||||||
|
setState(() {
|
||||||
|
_subspaces = [
|
||||||
|
..._subspaces,
|
||||||
|
Subspace(
|
||||||
|
name: name,
|
||||||
|
uuid: const Uuid().v4(),
|
||||||
|
productAllocations: const [],
|
||||||
|
),
|
||||||
|
];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleSubspaceDeleted(String uuid) => setState(
|
||||||
|
() => _subspaces = _subspaces.where((s) => s.uuid != uuid).toList(),
|
||||||
|
);
|
||||||
|
|
||||||
|
void _handleSave() {
|
||||||
|
widget.onSave(_subspaces);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: const SelectableText('Create Sub Spaces'),
|
||||||
|
content: Column(
|
||||||
|
spacing: 12,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
SubSpacesInput(
|
||||||
|
subSpaces: _subspaces,
|
||||||
|
onSubspaceAdded: _handleSubspaceAdded,
|
||||||
|
onSubspaceDeleted: _handleSubspaceDeleted,
|
||||||
|
),
|
||||||
|
AnimatedSwitcher(
|
||||||
|
duration: const Duration(milliseconds: 100),
|
||||||
|
child: Visibility(
|
||||||
|
key: ValueKey(_hasDuplicateNames),
|
||||||
|
visible: _hasDuplicateNames,
|
||||||
|
child: const SelectableText(
|
||||||
|
'Error: Duplicate subspace names are not allowed.',
|
||||||
|
style: TextStyle(color: Colors.red),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
SpaceDetailsActionButtons(
|
||||||
|
onSave: _hasDuplicateNames ? null : _handleSave,
|
||||||
|
onCancel: Navigator.of(context).pop,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,107 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/subspace_chip.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
|
class SubSpacesInput extends StatefulWidget {
|
||||||
|
const SubSpacesInput({
|
||||||
|
super.key,
|
||||||
|
required this.subSpaces,
|
||||||
|
required this.onSubspaceAdded,
|
||||||
|
required this.onSubspaceDeleted,
|
||||||
|
});
|
||||||
|
|
||||||
|
final List<Subspace> subSpaces;
|
||||||
|
final void Function(String name) onSubspaceAdded;
|
||||||
|
final void Function(String uuid) onSubspaceDeleted;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SubSpacesInput> createState() => _SubSpacesInputState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SubSpacesInputState extends State<SubSpacesInput> {
|
||||||
|
late final TextEditingController _subspaceNameController;
|
||||||
|
late final FocusNode _focusNode;
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_subspaceNameController = TextEditingController();
|
||||||
|
_focusNode = FocusNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_subspaceNameController.dispose();
|
||||||
|
_focusNode.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
width: context.screenWidth * 0.35,
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 10,
|
||||||
|
horizontal: 16,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: ColorsManager.boxColor,
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
child: Wrap(
|
||||||
|
spacing: 8,
|
||||||
|
runSpacing: 8,
|
||||||
|
alignment: WrapAlignment.start,
|
||||||
|
crossAxisAlignment: WrapCrossAlignment.center,
|
||||||
|
children: [
|
||||||
|
...widget.subSpaces.asMap().entries.map(
|
||||||
|
(entry) {
|
||||||
|
final index = entry.key;
|
||||||
|
final subSpace = entry.value;
|
||||||
|
|
||||||
|
final lowerName = subSpace.name.toLowerCase();
|
||||||
|
|
||||||
|
final duplicateIndices = widget.subSpaces
|
||||||
|
.asMap()
|
||||||
|
.entries
|
||||||
|
.where((e) => e.value.name.toLowerCase() == lowerName)
|
||||||
|
.map((e) => e.key)
|
||||||
|
.toList();
|
||||||
|
final isDuplicate = duplicateIndices.length > 1 &&
|
||||||
|
duplicateIndices.indexOf(index) != 0;
|
||||||
|
return SubspaceChip(
|
||||||
|
subSpace: subSpace,
|
||||||
|
isDuplicate: isDuplicate,
|
||||||
|
onDeleted: () => widget.onSubspaceDeleted(subSpace.uuid),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 200,
|
||||||
|
child: TextField(
|
||||||
|
focusNode: _focusNode,
|
||||||
|
controller: _subspaceNameController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: InputBorder.none,
|
||||||
|
hintText: widget.subSpaces.isEmpty ? 'Please enter the name' : null,
|
||||||
|
hintStyle: context.textTheme.bodySmall?.copyWith(
|
||||||
|
color: ColorsManager.lightGrayColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onSubmitted: (value) {
|
||||||
|
final trimmedValue = value.trim();
|
||||||
|
if (trimmedValue.isNotEmpty) {
|
||||||
|
widget.onSubspaceAdded(trimmedValue);
|
||||||
|
_subspaceNameController.clear();
|
||||||
|
_focusNode.requestFocus();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
style: context.textTheme.bodyMedium,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
|
class SubspaceChip extends StatelessWidget {
|
||||||
|
const SubspaceChip({
|
||||||
|
required this.subSpace,
|
||||||
|
required this.isDuplicate,
|
||||||
|
required this.onDeleted,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Subspace subSpace;
|
||||||
|
final bool isDuplicate;
|
||||||
|
final void Function() onDeleted;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Chip(
|
||||||
|
label: Text(
|
||||||
|
subSpace.name,
|
||||||
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
|
color: isDuplicate ? ColorsManager.red : ColorsManager.spaceColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
backgroundColor: ColorsManager.whiteColors,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
side: BorderSide(
|
||||||
|
color: isDuplicate ? ColorsManager.red : ColorsManager.transparentColor,
|
||||||
|
width: 0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
deleteIcon: Container(
|
||||||
|
padding: const EdgeInsetsDirectional.all(1),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
border: Border.all(
|
||||||
|
color: ColorsManager.lightGrayColor,
|
||||||
|
width: 1.5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: const FittedBox(
|
||||||
|
fit: BoxFit.scaleDown,
|
||||||
|
child: Icon(
|
||||||
|
Icons.close,
|
||||||
|
color: ColorsManager.lightGrayColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onDeleted: onDeleted,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,171 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/update_space/presentation/bloc/space_details_model_bloc/space_details_model_bloc.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
|
class SubspaceNameDisplayWidget extends StatefulWidget {
|
||||||
|
const SubspaceNameDisplayWidget({super.key, required this.subSpace});
|
||||||
|
|
||||||
|
final Subspace subSpace;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SubspaceNameDisplayWidget> createState() =>
|
||||||
|
_SubspaceNameDisplayWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SubspaceNameDisplayWidgetState extends State<SubspaceNameDisplayWidget> {
|
||||||
|
late final TextEditingController _controller;
|
||||||
|
late final FocusNode _focusNode;
|
||||||
|
bool _isEditing = false;
|
||||||
|
bool _hasDuplicateName = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
_controller = TextEditingController(text: widget.subSpace.name);
|
||||||
|
_focusNode = FocusNode();
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_controller.dispose();
|
||||||
|
_focusNode.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _checkForDuplicateName(String name) {
|
||||||
|
final bloc = context.read<SpaceDetailsModelBloc>();
|
||||||
|
return bloc.state.subspaces
|
||||||
|
.where((s) => s.uuid != widget.subSpace.uuid)
|
||||||
|
.any((s) => s.name.toLowerCase() == name.toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleNameChange(String value) {
|
||||||
|
setState(() {
|
||||||
|
_hasDuplicateName = _checkForDuplicateName(value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _tryToFinishEditing() {
|
||||||
|
if (!_hasDuplicateName) {
|
||||||
|
_onFinishEditing();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _tryToSubmit(String value) {
|
||||||
|
if (_hasDuplicateName) return;
|
||||||
|
|
||||||
|
final bloc = context.read<SpaceDetailsModelBloc>();
|
||||||
|
bloc.add(
|
||||||
|
UpdateSpaceDetailsSubspaces(
|
||||||
|
bloc.state.subspaces
|
||||||
|
.map(
|
||||||
|
(e) => e.uuid == widget.subSpace.uuid ? e.copyWith(name: value) : e,
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
_onFinishEditing();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final textStyle = context.textTheme.bodySmall?.copyWith(
|
||||||
|
color: ColorsManager.spaceColor,
|
||||||
|
);
|
||||||
|
return InkWell(
|
||||||
|
onTap: () {
|
||||||
|
setState(() => _isEditing = true);
|
||||||
|
_focusNode.requestFocus();
|
||||||
|
},
|
||||||
|
child: Chip(
|
||||||
|
backgroundColor: ColorsManager.whiteColors,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
side: const BorderSide(color: ColorsManager.transparentColor),
|
||||||
|
),
|
||||||
|
onDeleted: () {
|
||||||
|
final bloc = context.read<SpaceDetailsModelBloc>();
|
||||||
|
bloc.add(
|
||||||
|
UpdateSpaceDetailsSubspaces(
|
||||||
|
bloc.state.subspaces
|
||||||
|
.where((s) => s.uuid != widget.subSpace.uuid)
|
||||||
|
.toList(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
deleteIcon: Container(
|
||||||
|
padding: const EdgeInsetsDirectional.all(1),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
border: Border.all(
|
||||||
|
color: ColorsManager.lightGrayColor,
|
||||||
|
width: 1.5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: const FittedBox(
|
||||||
|
child: Icon(
|
||||||
|
Icons.close,
|
||||||
|
color: ColorsManager.lightGrayColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
label: Visibility(
|
||||||
|
visible: _isEditing,
|
||||||
|
replacement: Text(
|
||||||
|
widget.subSpace.name,
|
||||||
|
style: textStyle,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: context.screenWidth * 0.065,
|
||||||
|
height: context.screenHeight * 0.025,
|
||||||
|
child: TextField(
|
||||||
|
focusNode: _focusNode,
|
||||||
|
controller: _controller,
|
||||||
|
style: textStyle?.copyWith(
|
||||||
|
color: _hasDuplicateName ? Colors.red : null,
|
||||||
|
),
|
||||||
|
decoration: const InputDecoration.collapsed(
|
||||||
|
hintText: '',
|
||||||
|
),
|
||||||
|
onChanged: _handleNameChange,
|
||||||
|
onTapOutside: (_) => _tryToFinishEditing(),
|
||||||
|
onSubmitted: _tryToSubmit,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (_hasDuplicateName)
|
||||||
|
AnimatedSwitcher(
|
||||||
|
duration: const Duration(milliseconds: 250),
|
||||||
|
child: Visibility(
|
||||||
|
key: ValueKey(_hasDuplicateName),
|
||||||
|
visible: _hasDuplicateName,
|
||||||
|
child: Text(
|
||||||
|
'Name already exists',
|
||||||
|
style: textStyle?.copyWith(
|
||||||
|
color: Colors.red,
|
||||||
|
fontSize: 8,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onFinishEditing() {
|
||||||
|
setState(() {
|
||||||
|
_isEditing = false;
|
||||||
|
_hasDuplicateName = false;
|
||||||
|
});
|
||||||
|
_focusNode.unfocus();
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/update_community/domain/params/update_community_param.dart';
|
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/update_community/domain/services/update_community_service.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/update_community/domain/services/update_community_service.dart';
|
||||||
import 'package:syncrow_web/services/api/api_exception.dart';
|
import 'package:syncrow_web/services/api/api_exception.dart';
|
||||||
import 'package:syncrow_web/services/api/http_service.dart';
|
import 'package:syncrow_web/services/api/http_service.dart';
|
||||||
@ -13,15 +13,15 @@ class RemoteUpdateCommunityService implements UpdateCommunityService {
|
|||||||
static const _defaultErrorMessage = 'Failed to update community';
|
static const _defaultErrorMessage = 'Failed to update community';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<CommunityModel> updateCommunity(UpdateCommunityParam param) async {
|
Future<CommunityModel> updateCommunity(CommunityModel param) async {
|
||||||
|
final endpoint = await _makeUrl(param.uuid);
|
||||||
try {
|
try {
|
||||||
final response = await _httpService.put(
|
await _httpService.put(
|
||||||
path: 'endpoint',
|
path: endpoint,
|
||||||
expectedResponseModel: (data) => CommunityModel.fromJson(
|
body: {'name': param.name},
|
||||||
data as Map<String, dynamic>,
|
expectedResponseModel: (data) => null,
|
||||||
),
|
|
||||||
);
|
);
|
||||||
return response;
|
return param;
|
||||||
} on DioException catch (e) {
|
} on DioException catch (e) {
|
||||||
final message = e.response?.data as Map<String, dynamic>?;
|
final message = e.response?.data as Map<String, dynamic>?;
|
||||||
final error = message?['error'] as Map<String, dynamic>?;
|
final error = message?['error'] as Map<String, dynamic>?;
|
||||||
@ -36,4 +36,12 @@ class RemoteUpdateCommunityService implements UpdateCommunityService {
|
|||||||
throw APIException(formattedErrorMessage);
|
throw APIException(formattedErrorMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<String> _makeUrl(String communityUuid) async {
|
||||||
|
final projectUuid = await ProjectManager.getProjectUUID();
|
||||||
|
if (projectUuid == null) {
|
||||||
|
throw APIException('Project UUID is not set');
|
||||||
|
}
|
||||||
|
return '/projects/$projectUuid/communities/$communityUuid';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
|
||||||
|
|
||||||
class UpdateCommunityParam extends Equatable {
|
|
||||||
const UpdateCommunityParam({required this.name});
|
|
||||||
|
|
||||||
final String name;
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object> get props => [name];
|
|
||||||
}
|
|
@ -1,6 +1,5 @@
|
|||||||
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/update_community/domain/params/update_community_param.dart';
|
|
||||||
|
|
||||||
abstract class UpdateCommunityService {
|
abstract class UpdateCommunityService {
|
||||||
Future<CommunityModel> updateCommunity(UpdateCommunityParam param);
|
Future<CommunityModel> updateCommunity(CommunityModel community);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/update_community/domain/params/update_community_param.dart';
|
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/update_community/domain/services/update_community_service.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/update_community/domain/services/update_community_service.dart';
|
||||||
import 'package:syncrow_web/services/api/api_exception.dart';
|
import 'package:syncrow_web/services/api/api_exception.dart';
|
||||||
|
|
||||||
@ -24,7 +23,7 @@ class UpdateCommunityBloc extends Bloc<UpdateCommunityEvent, UpdateCommunityStat
|
|||||||
emit(UpdateCommunityLoading());
|
emit(UpdateCommunityLoading());
|
||||||
try {
|
try {
|
||||||
final updatedCommunity = await _updateCommunityService.updateCommunity(
|
final updatedCommunity = await _updateCommunityService.updateCommunity(
|
||||||
event.param,
|
event.communityModel,
|
||||||
);
|
);
|
||||||
emit(UpdateCommunitySuccess(updatedCommunity));
|
emit(UpdateCommunitySuccess(updatedCommunity));
|
||||||
} on APIException catch (e) {
|
} on APIException catch (e) {
|
||||||
|
@ -8,10 +8,10 @@ sealed class UpdateCommunityEvent extends Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final class UpdateCommunity extends UpdateCommunityEvent {
|
final class UpdateCommunity extends UpdateCommunityEvent {
|
||||||
const UpdateCommunity(this.param);
|
const UpdateCommunity(this.communityModel);
|
||||||
|
|
||||||
final UpdateCommunityParam param;
|
final CommunityModel communityModel ;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [param];
|
List<Object> get props => [communityModel];
|
||||||
}
|
}
|
||||||
|
@ -21,10 +21,10 @@ final class UpdateCommunitySuccess extends UpdateCommunityState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final class UpdateCommunityFailure extends UpdateCommunityState {
|
final class UpdateCommunityFailure extends UpdateCommunityState {
|
||||||
final String message;
|
final String errorMessage;
|
||||||
|
|
||||||
const UpdateCommunityFailure(this.message);
|
const UpdateCommunityFailure(this.errorMessage);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [message];
|
List<Object> get props => [errorMessage];
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,71 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/main_module/shared/helpers/space_management_community_dialog_helper.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/main_module/shared/widgets/community_dialog.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/bloc/communities_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/communities_tree_selection_bloc/communities_tree_selection_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/update_community/data/services/remote_update_community_service.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/update_community/presentation/bloc/update_community_bloc.dart';
|
||||||
|
import 'package:syncrow_web/services/api/http_service.dart';
|
||||||
|
|
||||||
|
class EditCommunityDialog extends StatelessWidget {
|
||||||
|
const EditCommunityDialog({
|
||||||
|
required this.community,
|
||||||
|
required this.parentContext,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final CommunityModel community;
|
||||||
|
final BuildContext parentContext;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (_) => UpdateCommunityBloc(
|
||||||
|
RemoteUpdateCommunityService(HTTPService()),
|
||||||
|
),
|
||||||
|
child: BlocConsumer<UpdateCommunityBloc, UpdateCommunityState>(
|
||||||
|
listener: (context, state) {
|
||||||
|
switch (state) {
|
||||||
|
case UpdateCommunityInitial() || UpdateCommunityLoading():
|
||||||
|
SpaceManagementCommunityDialogHelper.showLoadingDialog(context);
|
||||||
|
break;
|
||||||
|
case UpdateCommunitySuccess(:final community):
|
||||||
|
_onUpdateCommunitySuccess(context, community);
|
||||||
|
break;
|
||||||
|
case UpdateCommunityFailure():
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
builder: (context, state) => CommunityDialog(
|
||||||
|
title: const Text('Edit Community'),
|
||||||
|
initialName: community.name,
|
||||||
|
errorMessage: state is UpdateCommunityFailure ? state.errorMessage : null,
|
||||||
|
onSubmit: (name) => context.read<UpdateCommunityBloc>().add(
|
||||||
|
UpdateCommunity(community.copyWith(name: name)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onUpdateCommunitySuccess(
|
||||||
|
BuildContext context,
|
||||||
|
CommunityModel community,
|
||||||
|
) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
SpaceManagementCommunityDialogHelper.showSuccessSnackBar(
|
||||||
|
context,
|
||||||
|
'${community.name} community updated successfully',
|
||||||
|
);
|
||||||
|
parentContext.read<CommunitiesBloc>().add(
|
||||||
|
CommunitiesUpdateCommunity(community),
|
||||||
|
);
|
||||||
|
parentContext.read<CommunitiesTreeSelectionBloc>().add(
|
||||||
|
SelectCommunityEvent(community: community),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
import 'package:bloc/bloc.dart';
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
||||||
|
|
||||||
|
part 'space_details_model_event.dart';
|
||||||
|
|
||||||
|
class SpaceDetailsModelBloc extends Bloc<SpaceDetailsModelEvent, SpaceDetailsModel> {
|
||||||
|
SpaceDetailsModelBloc({
|
||||||
|
required SpaceDetailsModel initialState,
|
||||||
|
}) : super(initialState) {
|
||||||
|
on<UpdateSpaceDetailsIcon>(_onUpdateSpaceDetailsIcon);
|
||||||
|
on<UpdateSpaceDetailsName>(_onUpdateSpaceDetailsName);
|
||||||
|
on<UpdateSpaceDetailsSubspaces>(_onUpdateSpaceDetailsSubspaces);
|
||||||
|
on<UpdateSpaceDetailsProductAllocations>(
|
||||||
|
_onUpdateSpaceDetailsProductAllocations);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onUpdateSpaceDetailsIcon(
|
||||||
|
UpdateSpaceDetailsIcon event,
|
||||||
|
Emitter<SpaceDetailsModel> emit,
|
||||||
|
) {
|
||||||
|
emit(state.copyWith(icon: event.icon));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onUpdateSpaceDetailsName(
|
||||||
|
UpdateSpaceDetailsName event,
|
||||||
|
Emitter<SpaceDetailsModel> emit,
|
||||||
|
) {
|
||||||
|
emit(state.copyWith(spaceName: event.name));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onUpdateSpaceDetailsSubspaces(
|
||||||
|
UpdateSpaceDetailsSubspaces event,
|
||||||
|
Emitter<SpaceDetailsModel> emit,
|
||||||
|
) {
|
||||||
|
emit(state.copyWith(subspaces: event.subspaces));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onUpdateSpaceDetailsProductAllocations(
|
||||||
|
UpdateSpaceDetailsProductAllocations event,
|
||||||
|
Emitter<SpaceDetailsModel> emit,
|
||||||
|
) {
|
||||||
|
emit(state.copyWith(productAllocations: event.productAllocations));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
part of 'space_details_model_bloc.dart';
|
||||||
|
|
||||||
|
sealed class SpaceDetailsModelEvent extends Equatable {
|
||||||
|
const SpaceDetailsModelEvent();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
final class UpdateSpaceDetailsIcon extends SpaceDetailsModelEvent {
|
||||||
|
const UpdateSpaceDetailsIcon(this.icon);
|
||||||
|
|
||||||
|
final String icon;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [icon];
|
||||||
|
}
|
||||||
|
|
||||||
|
final class UpdateSpaceDetailsName extends SpaceDetailsModelEvent {
|
||||||
|
const UpdateSpaceDetailsName(this.name);
|
||||||
|
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [name];
|
||||||
|
}
|
||||||
|
|
||||||
|
final class UpdateSpaceDetailsSubspaces extends SpaceDetailsModelEvent {
|
||||||
|
const UpdateSpaceDetailsSubspaces(this.subspaces);
|
||||||
|
|
||||||
|
final List<Subspace> subspaces;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [subspaces];
|
||||||
|
}
|
||||||
|
|
||||||
|
final class UpdateSpaceDetailsProductAllocations extends SpaceDetailsModelEvent {
|
||||||
|
const UpdateSpaceDetailsProductAllocations(this.productAllocations);
|
||||||
|
|
||||||
|
final List<ProductAllocation> productAllocations;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [productAllocations];
|
||||||
|
}
|
@ -52,4 +52,7 @@ final myTheme = ThemeData(
|
|||||||
borderRadius: BorderRadius.circular(4),
|
borderRadius: BorderRadius.circular(4),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
dialogTheme: const DialogThemeData(
|
||||||
|
backgroundColor: ColorsManager.whiteColors,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
Reference in New Issue
Block a user