Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ jobs:
BASE_HREF: "/fl_basic_accounting/"
BUILD_NUMBER: ${{ steps.build-number.outputs.build_number }}
run: |
flutter build web --release --base-href ${{ env.BASE_HREF }} --build-number ${{ env.BUILD_NUMBER }}
flutter build web --release --wasm --base-href ${{ env.BASE_HREF }} --build-number ${{ env.BUILD_NUMBER }}

- name: 🚀 Deploy to Github Pages
uses: peaceiris/actions-gh-pages@v3
Expand Down
11 changes: 8 additions & 3 deletions build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,17 @@ targets:
# https://pub.dev/packages/freezed
freezed:
options:
to_json: false
copy_with: false
copy_with: true
equal: true

# https://pub.dev/packages/json_serializable
json_serializable:
options:
explicit_to_json: true
# field_rename: snake
field_rename: snake

drift_dev:
options:
databases:
app_database: lib/infrastructures/libs/data/local/libs/database.dart
store_date_time_values_as_text: true
2 changes: 1 addition & 1 deletion dangerfile.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ void checkTitle() {
final isTitleValid = RegExp(
'^(${GitlintConfig.types.join("|")})'
'(\\((${GitlintConfig.scopes.join(("|"))})(\\/(${GitlintConfig.scopes.join(("|"))}))*\\))?'
"(!|): (.*\\S )?"
"(!)?: (.*\\S )?"
'(${GitlintConfig.issuePrefixes.join("|")})\\d{1,6}((\\.\\d+){1,2})?\$',
).hasMatch(pr.title);

Expand Down
3 changes: 3 additions & 0 deletions devtools_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:
3 changes: 1 addition & 2 deletions lib/domain/entities.dart
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export "entities/product_entity.dart";
export "entities/user_entity.dart";
export "entities/modules.dart";
1 change: 1 addition & 0 deletions lib/domain/entities/libs/types.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export "types/entity.dart";
38 changes: 38 additions & 0 deletions lib/domain/entities/libs/types/entity.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import "package:freezed_annotation/freezed_annotation.dart";

import "package:app/libs/decorators/freezed.dart";
export "package:app/libs/decorators/freezed.dart" show dateTimeProp;

part "entity.freezed.dart";
part "entity.g.dart";

@Freezed(copyWith: false)
@JsonSerializable()
class Entity with _$Entity {
/// Entity identifier.
///
/// Should only be provided if you want to update this entity.
@override
final int id;

/// Timestamp when this entity was created.
@dateTimeProp
@override
late final DateTime createdAt;

/// Timestamp when this entity was last updated.
@dateTimeProp
@override
late final DateTime updatedAt;

Entity({this.id = 0, DateTime? createdAt, DateTime? updatedAt}) {
final now = DateTime.now().toUtc();

this.createdAt = createdAt ?? now;
this.updatedAt = updatedAt ?? now;
}

factory Entity.fromJson(Map<String, dynamic> json) => _$EntityFromJson(json);

Map<String, dynamic> toJson() => _$EntityToJson(this);
}
2 changes: 2 additions & 0 deletions lib/domain/entities/modules.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export "libs/types.dart" show Entity;
export "modules/project.dart";
30 changes: 30 additions & 0 deletions lib/domain/entities/modules/project.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import "package:freezed_annotation/freezed_annotation.dart";

import "package:app/domain/entities/libs/types.dart";

part "project.freezed.dart";
part "project.g.dart";

@freezed
@JsonSerializable()
final class Project extends Entity with _$Project {
@override
final String name;

@override
final String? description;

Project({
super.id,
super.createdAt,
super.updatedAt,
required this.name,
this.description,
});

factory Project.fromJson(Map<String, dynamic> json) =>
_$ProjectFromJson(json);

@override
Map<String, dynamic> toJson() => _$ProjectToJson(this);
}
12 changes: 0 additions & 12 deletions lib/domain/entities/product_entity.dart

This file was deleted.

8 changes: 0 additions & 8 deletions lib/domain/entities/user_entity.dart

This file was deleted.

1 change: 1 addition & 0 deletions lib/domain/repositories.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export "repositories/modules.dart";
1 change: 1 addition & 0 deletions lib/domain/repositories/libs/types.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export "types/crud_repository.dart";
26 changes: 26 additions & 0 deletions lib/domain/repositories/libs/types/crud_repository.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import "package:app/domain/entities.dart";

/// Base class for repositories that may need CRUD methods.
abstract class CrudRepository<T extends Entity> {
const CrudRepository();

/// Store [entity] into database.
Future<T> create(T entity);

/// Return entity by [id].
Future<T?> readById(int id);

/// Return list of entities from database.
Future<List<T>> readAll();

/// Update entity with [updatedEntity] values.
///
/// Make use of [Entity.copyWith] method to make sure the [updatedEntity] to
/// safely clone the [Entity.id] for update purpose.
Future<T?> update(T updatedEntity);

/// Delete entity by [id].
///
/// Return deleted entity.
Future<T?> delete(int id);
}
1 change: 1 addition & 0 deletions lib/domain/repositories/modules.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export "modules/projects_repository.dart";
8 changes: 8 additions & 0 deletions lib/domain/repositories/modules/projects_repository.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import "package:app/domain/entities.dart";
import "package:app/domain/repositories/libs/types.dart";

abstract interface class ProjectsRepository extends CrudRepository<Project> {
// Should add other methods to handle complex projects use cases.

Stream<List<Project>> readAllAsStream();
}
1 change: 1 addition & 0 deletions lib/infrastructures/libs/data.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export "data/local.dart";
1 change: 1 addition & 0 deletions lib/infrastructures/libs/data/local.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export "local/modules.dart";
27 changes: 27 additions & 0 deletions lib/infrastructures/libs/data/local/libs/database.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import "package:drift/drift.dart";
import "package:drift_flutter/drift_flutter.dart";
import "package:injectable/injectable.dart";

import "tables.dart";

part "database.g.dart";

@lazySingleton
@DriftDatabase(tables: [Projects])
class AppDatabase extends _$AppDatabase {
AppDatabase() : super(_openConnection());

@override
int get schemaVersion => 1;

static QueryExecutor _openConnection() {
return driftDatabase(
name: "app_database",
native: const DriftNativeOptions(),
web: DriftWebOptions(
sqlite3Wasm: Uri.parse("sqlite3.wasm"),
driftWorker: Uri.parse("drift_worker.dart.js"),
),
);
}
}
1 change: 1 addition & 0 deletions lib/infrastructures/libs/data/local/libs/tables.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export "tables/modules.dart";
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export "types/default_table_columns.dart";
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import "package:drift/drift.dart";

/// Must be used on every table definitions.
///
/// Contains [id] as primary key with [createdAt] and [updatedAt] as timestamps.
mixin DefaultTableColumns on Table {
late final id = integer().autoIncrement()();
late final createdAt = dateTime().withDefault(currentDateAndTime)();
late final updatedAt = dateTime().withDefault(currentDateAndTime)();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export "modules/projects.dart";
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import "package:drift/drift.dart";

import "../libs/types.dart";

@DataClassName("ProjectModel")
class Projects extends Table with DefaultTableColumns {
late final name = text()();
late final description = text().nullable()();
}
1 change: 1 addition & 0 deletions lib/infrastructures/libs/data/local/modules.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export "modules/projects_local_data.dart";
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import "package:drift/drift.dart";
import "package:injectable/injectable.dart";

import "package:app/domain/entities.dart";

import "../libs/database.dart";
import "../libs/tables.dart";

part "projects_local_data.g.dart";

@lazySingleton
@DriftAccessor(tables: [Projects])
class ProjectsLocalData extends DatabaseAccessor<AppDatabase>
with _$ProjectsLocalDataMixin {
ProjectsLocalData(super.db);

Future<ProjectModel> create({
required String name,
String? description,
}) async {
final Value<String?> descriptionValue =
description?.isNotEmpty ?? true ? Value(description) : Value(null);

return projects.insertReturning(
ProjectsCompanion.insert(name: name, description: descriptionValue),
);
}

Future<List<ProjectModel>> readAll() async {
return projects.select().get();
}

Stream<List<ProjectModel>> readAllAsStream() {
return projects.select().watch();
}

Future<ProjectModel?> readById(int id) async {
return (projects.select()..where((tbl) => tbl.id.equals(id)))
.getSingleOrNull();
}

Future<ProjectModel?> updateById(
int id, {
String? name,
String? description,
}) async {
final updatedAt = DateTime.now().toUtc();

final Value<String> nameValue = name == null ? Value.absent() : Value(name);
final Value<String?> descriptionValue =
description?.isNotEmpty ?? true ? Value(description) : Value(null);

final results = await (projects.update()..where((e) => e.id.equals(id)))
.writeReturning(
ProjectsCompanion(
name: nameValue,
description: descriptionValue,
updatedAt: Value(updatedAt),
),
);

return results.firstOrNull;
}

Future<ProjectModel?> deleteById(int id) async {
final results =
await (projects.delete()..where((e) => e.id.equals(id))).goAndReturn();

return results.firstOrNull;
}
}

extension ProjectModelX on ProjectModel {
Project toEntity() {
return Project(
id: id,
name: name,
description: description,
createdAt: createdAt,
updatedAt: updatedAt,
);
}
}
63 changes: 63 additions & 0 deletions lib/infrastructures/modules/repositories/projects_repository.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import "package:injectable/injectable.dart";

import "package:app/domain/entities.dart";
import "package:app/domain/repositories.dart";
import "package:app/infrastructures/libs/data.dart";

@LazySingleton(as: ProjectsRepository)
final class ProjectsRepositoryImpl implements ProjectsRepository {
final ProjectsLocalData _projectsLocalData;

const ProjectsRepositoryImpl(this._projectsLocalData);

@override
Future<Project> create(Project entity) async {
final result = await _projectsLocalData.create(
name: entity.name,
description: entity.description,
);

return result.toEntity();
}

@override
Future<Project?> delete(int id) async {
final result = await _projectsLocalData.deleteById(id);

return result?.toEntity();
}

@override
Future<List<Project>> readAll() async {
final results = await _projectsLocalData.readAll();

return results.map((e) => e.toEntity()).toList();
}

@override
Future<Project?> readById(int id) async {
final result = await _projectsLocalData.readById(id);

return result?.toEntity();
}

@override
Future<Project?> update(Project updatedEntity) async {
final Project(:id, :name, :description) = updatedEntity;

final result = await _projectsLocalData.updateById(
id,
name: name,
description: description,
);

return result?.toEntity();
}

@override
Stream<List<Project>> readAllAsStream() {
return _projectsLocalData.readAllAsStream().map(
(results) => results.map((e) => e.toEntity()).toList(),
);
}
}
Loading
Loading