1
0
mirror of https://github.com/immich-app/immich.git synced 2025-08-10 23:22:22 +02:00

feat(server/web): jobs clear button + queue status (#2144)

* feat(server/web): jobs clear button + queue status

* adjust design and colors

* Adjust some styling

* show status next to buttons instead of on top

* Update rounded corner for badge

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
Michel Heusschen
2023-04-01 22:46:07 +02:00
committed by GitHub
parent d04f340b5b
commit b06ddec2d5
32 changed files with 722 additions and 242 deletions

View File

@@ -53,6 +53,7 @@ doc/JobCommand.md
doc/JobCommandDto.md
doc/JobCountsDto.md
doc/JobName.md
doc/JobStatusDto.md
doc/LoginCredentialDto.md
doc/LoginResponseDto.md
doc/LogoutResponseDto.md
@@ -60,6 +61,7 @@ doc/OAuthApi.md
doc/OAuthCallbackDto.md
doc/OAuthConfigDto.md
doc/OAuthConfigResponseDto.md
doc/QueueStatusDto.md
doc/RemoveAssetsDto.md
doc/SearchAlbumResponseDto.md
doc/SearchApi.md
@@ -170,12 +172,14 @@ lib/model/job_command.dart
lib/model/job_command_dto.dart
lib/model/job_counts_dto.dart
lib/model/job_name.dart
lib/model/job_status_dto.dart
lib/model/login_credential_dto.dart
lib/model/login_response_dto.dart
lib/model/logout_response_dto.dart
lib/model/o_auth_callback_dto.dart
lib/model/o_auth_config_dto.dart
lib/model/o_auth_config_response_dto.dart
lib/model/queue_status_dto.dart
lib/model/remove_assets_dto.dart
lib/model/search_album_response_dto.dart
lib/model/search_asset_dto.dart
@@ -264,6 +268,7 @@ test/job_command_dto_test.dart
test/job_command_test.dart
test/job_counts_dto_test.dart
test/job_name_test.dart
test/job_status_dto_test.dart
test/login_credential_dto_test.dart
test/login_response_dto_test.dart
test/logout_response_dto_test.dart
@@ -271,6 +276,7 @@ test/o_auth_api_test.dart
test/o_auth_callback_dto_test.dart
test/o_auth_config_dto_test.dart
test/o_auth_config_response_dto_test.dart
test/queue_status_dto_test.dart
test/remove_assets_dto_test.dart
test/search_album_response_dto_test.dart
test/search_api_test.dart

View File

@@ -200,12 +200,14 @@ Class | Method | HTTP request | Description
- [JobCommandDto](doc//JobCommandDto.md)
- [JobCountsDto](doc//JobCountsDto.md)
- [JobName](doc//JobName.md)
- [JobStatusDto](doc//JobStatusDto.md)
- [LoginCredentialDto](doc//LoginCredentialDto.md)
- [LoginResponseDto](doc//LoginResponseDto.md)
- [LogoutResponseDto](doc//LogoutResponseDto.md)
- [OAuthCallbackDto](doc//OAuthCallbackDto.md)
- [OAuthConfigDto](doc//OAuthConfigDto.md)
- [OAuthConfigResponseDto](doc//OAuthConfigResponseDto.md)
- [QueueStatusDto](doc//QueueStatusDto.md)
- [RemoveAssetsDto](doc//RemoveAssetsDto.md)
- [SearchAlbumResponseDto](doc//SearchAlbumResponseDto.md)
- [SearchAssetDto](doc//SearchAssetDto.md)

View File

@@ -8,14 +8,14 @@ import 'package:openapi/api.dart';
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**thumbnailGenerationQueue** | [**JobCountsDto**](JobCountsDto.md) | |
**metadataExtractionQueue** | [**JobCountsDto**](JobCountsDto.md) | |
**videoConversionQueue** | [**JobCountsDto**](JobCountsDto.md) | |
**objectTaggingQueue** | [**JobCountsDto**](JobCountsDto.md) | |
**clipEncodingQueue** | [**JobCountsDto**](JobCountsDto.md) | |
**storageTemplateMigrationQueue** | [**JobCountsDto**](JobCountsDto.md) | |
**backgroundTaskQueue** | [**JobCountsDto**](JobCountsDto.md) | |
**searchQueue** | [**JobCountsDto**](JobCountsDto.md) | |
**thumbnailGenerationQueue** | [**JobStatusDto**](JobStatusDto.md) | |
**metadataExtractionQueue** | [**JobStatusDto**](JobStatusDto.md) | |
**videoConversionQueue** | [**JobStatusDto**](JobStatusDto.md) | |
**objectTaggingQueue** | [**JobStatusDto**](JobStatusDto.md) | |
**clipEncodingQueue** | [**JobStatusDto**](JobStatusDto.md) | |
**storageTemplateMigrationQueue** | [**JobStatusDto**](JobStatusDto.md) | |
**backgroundTaskQueue** | [**JobStatusDto**](JobStatusDto.md) | |
**searchQueue** | [**JobStatusDto**](JobStatusDto.md) | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@@ -63,7 +63,7 @@ This endpoint does not need any parameter.
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **sendJobCommand**
> sendJobCommand(jobId, jobCommandDto)
> JobStatusDto sendJobCommand(jobId, jobCommandDto)
@@ -88,7 +88,8 @@ final jobId = ; // JobName |
final jobCommandDto = JobCommandDto(); // JobCommandDto |
try {
api_instance.sendJobCommand(jobId, jobCommandDto);
final result = api_instance.sendJobCommand(jobId, jobCommandDto);
print(result);
} catch (e) {
print('Exception when calling JobApi->sendJobCommand: $e\n');
}
@@ -103,7 +104,7 @@ Name | Type | Description | Notes
### Return type
void (empty response body)
[**JobStatusDto**](JobStatusDto.md)
### Authorization
@@ -112,7 +113,7 @@ void (empty response body)
### HTTP request headers
- **Content-Type**: application/json
- **Accept**: Not defined
- **Accept**: application/json
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)

16
mobile/openapi/doc/JobStatusDto.md generated Normal file
View File

@@ -0,0 +1,16 @@
# openapi.model.JobStatusDto
## Load the model package
```dart
import 'package:openapi/api.dart';
```
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**jobCounts** | [**JobCountsDto**](JobCountsDto.md) | |
**queueStatus** | [**QueueStatusDto**](QueueStatusDto.md) | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

16
mobile/openapi/doc/QueueStatusDto.md generated Normal file
View File

@@ -0,0 +1,16 @@
# openapi.model.QueueStatusDto
## Load the model package
```dart
import 'package:openapi/api.dart';
```
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**isActive** | **bool** | |
**isPaused** | **bool** | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@@ -86,12 +86,14 @@ part 'model/job_command.dart';
part 'model/job_command_dto.dart';
part 'model/job_counts_dto.dart';
part 'model/job_name.dart';
part 'model/job_status_dto.dart';
part 'model/login_credential_dto.dart';
part 'model/login_response_dto.dart';
part 'model/logout_response_dto.dart';
part 'model/o_auth_callback_dto.dart';
part 'model/o_auth_config_dto.dart';
part 'model/o_auth_config_response_dto.dart';
part 'model/queue_status_dto.dart';
part 'model/remove_assets_dto.dart';
part 'model/search_album_response_dto.dart';
part 'model/search_asset_dto.dart';

View File

@@ -102,10 +102,18 @@ class JobApi {
/// * [JobName] jobId (required):
///
/// * [JobCommandDto] jobCommandDto (required):
Future<void> sendJobCommand(JobName jobId, JobCommandDto jobCommandDto,) async {
Future<JobStatusDto?> sendJobCommand(JobName jobId, JobCommandDto jobCommandDto,) async {
final response = await sendJobCommandWithHttpInfo(jobId, jobCommandDto,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
// When a remote server returns no body with a status of 204, we shall not decode it.
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'JobStatusDto',) as JobStatusDto;
}
return null;
}
}

View File

@@ -280,6 +280,8 @@ class ApiClient {
return JobCountsDto.fromJson(value);
case 'JobName':
return JobNameTypeTransformer().decode(value);
case 'JobStatusDto':
return JobStatusDto.fromJson(value);
case 'LoginCredentialDto':
return LoginCredentialDto.fromJson(value);
case 'LoginResponseDto':
@@ -292,6 +294,8 @@ class ApiClient {
return OAuthConfigDto.fromJson(value);
case 'OAuthConfigResponseDto':
return OAuthConfigResponseDto.fromJson(value);
case 'QueueStatusDto':
return QueueStatusDto.fromJson(value);
case 'RemoveAssetsDto':
return RemoveAssetsDto.fromJson(value);
case 'SearchAlbumResponseDto':

View File

@@ -23,21 +23,21 @@ class AllJobStatusResponseDto {
required this.searchQueue,
});
JobCountsDto thumbnailGenerationQueue;
JobStatusDto thumbnailGenerationQueue;
JobCountsDto metadataExtractionQueue;
JobStatusDto metadataExtractionQueue;
JobCountsDto videoConversionQueue;
JobStatusDto videoConversionQueue;
JobCountsDto objectTaggingQueue;
JobStatusDto objectTaggingQueue;
JobCountsDto clipEncodingQueue;
JobStatusDto clipEncodingQueue;
JobCountsDto storageTemplateMigrationQueue;
JobStatusDto storageTemplateMigrationQueue;
JobCountsDto backgroundTaskQueue;
JobStatusDto backgroundTaskQueue;
JobCountsDto searchQueue;
JobStatusDto searchQueue;
@override
bool operator ==(Object other) => identical(this, other) || other is AllJobStatusResponseDto &&
@@ -97,14 +97,14 @@ class AllJobStatusResponseDto {
}());
return AllJobStatusResponseDto(
thumbnailGenerationQueue: JobCountsDto.fromJson(json[r'thumbnail-generation-queue'])!,
metadataExtractionQueue: JobCountsDto.fromJson(json[r'metadata-extraction-queue'])!,
videoConversionQueue: JobCountsDto.fromJson(json[r'video-conversion-queue'])!,
objectTaggingQueue: JobCountsDto.fromJson(json[r'object-tagging-queue'])!,
clipEncodingQueue: JobCountsDto.fromJson(json[r'clip-encoding-queue'])!,
storageTemplateMigrationQueue: JobCountsDto.fromJson(json[r'storage-template-migration-queue'])!,
backgroundTaskQueue: JobCountsDto.fromJson(json[r'background-task-queue'])!,
searchQueue: JobCountsDto.fromJson(json[r'search-queue'])!,
thumbnailGenerationQueue: JobStatusDto.fromJson(json[r'thumbnail-generation-queue'])!,
metadataExtractionQueue: JobStatusDto.fromJson(json[r'metadata-extraction-queue'])!,
videoConversionQueue: JobStatusDto.fromJson(json[r'video-conversion-queue'])!,
objectTaggingQueue: JobStatusDto.fromJson(json[r'object-tagging-queue'])!,
clipEncodingQueue: JobStatusDto.fromJson(json[r'clip-encoding-queue'])!,
storageTemplateMigrationQueue: JobStatusDto.fromJson(json[r'storage-template-migration-queue'])!,
backgroundTaskQueue: JobStatusDto.fromJson(json[r'background-task-queue'])!,
searchQueue: JobStatusDto.fromJson(json[r'search-queue'])!,
);
}
return null;

View File

@@ -0,0 +1,119 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.12
// ignore_for_file: unused_element, unused_import
// ignore_for_file: always_put_required_named_parameters_first
// ignore_for_file: constant_identifier_names
// ignore_for_file: lines_longer_than_80_chars
part of openapi.api;
class JobStatusDto {
/// Returns a new [JobStatusDto] instance.
JobStatusDto({
required this.jobCounts,
required this.queueStatus,
});
JobCountsDto jobCounts;
QueueStatusDto queueStatus;
@override
bool operator ==(Object other) => identical(this, other) || other is JobStatusDto &&
other.jobCounts == jobCounts &&
other.queueStatus == queueStatus;
@override
int get hashCode =>
// ignore: unnecessary_parenthesis
(jobCounts.hashCode) +
(queueStatus.hashCode);
@override
String toString() => 'JobStatusDto[jobCounts=$jobCounts, queueStatus=$queueStatus]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json[r'jobCounts'] = this.jobCounts;
json[r'queueStatus'] = this.queueStatus;
return json;
}
/// Returns a new [JobStatusDto] instance and imports its values from
/// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods
static JobStatusDto? fromJson(dynamic value) {
if (value is Map) {
final json = value.cast<String, dynamic>();
// Ensure that the map contains the required keys.
// Note 1: the values aren't checked for validity beyond being non-null.
// Note 2: this code is stripped in release mode!
assert(() {
requiredKeys.forEach((key) {
assert(json.containsKey(key), 'Required key "JobStatusDto[$key]" is missing from JSON.');
assert(json[key] != null, 'Required key "JobStatusDto[$key]" has a null value in JSON.');
});
return true;
}());
return JobStatusDto(
jobCounts: JobCountsDto.fromJson(json[r'jobCounts'])!,
queueStatus: QueueStatusDto.fromJson(json[r'queueStatus'])!,
);
}
return null;
}
static List<JobStatusDto>? listFromJson(dynamic json, {bool growable = false,}) {
final result = <JobStatusDto>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = JobStatusDto.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
static Map<String, JobStatusDto> mapFromJson(dynamic json) {
final map = <String, JobStatusDto>{};
if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) {
final value = JobStatusDto.fromJson(entry.value);
if (value != null) {
map[entry.key] = value;
}
}
}
return map;
}
// maps a json object with a list of JobStatusDto-objects as value to a dart map
static Map<String, List<JobStatusDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<JobStatusDto>>{};
if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) {
final value = JobStatusDto.listFromJson(entry.value, growable: growable,);
if (value != null) {
map[entry.key] = value;
}
}
}
return map;
}
/// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{
'jobCounts',
'queueStatus',
};
}

View File

@@ -0,0 +1,119 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.12
// ignore_for_file: unused_element, unused_import
// ignore_for_file: always_put_required_named_parameters_first
// ignore_for_file: constant_identifier_names
// ignore_for_file: lines_longer_than_80_chars
part of openapi.api;
class QueueStatusDto {
/// Returns a new [QueueStatusDto] instance.
QueueStatusDto({
required this.isActive,
required this.isPaused,
});
bool isActive;
bool isPaused;
@override
bool operator ==(Object other) => identical(this, other) || other is QueueStatusDto &&
other.isActive == isActive &&
other.isPaused == isPaused;
@override
int get hashCode =>
// ignore: unnecessary_parenthesis
(isActive.hashCode) +
(isPaused.hashCode);
@override
String toString() => 'QueueStatusDto[isActive=$isActive, isPaused=$isPaused]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json[r'isActive'] = this.isActive;
json[r'isPaused'] = this.isPaused;
return json;
}
/// Returns a new [QueueStatusDto] instance and imports its values from
/// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods
static QueueStatusDto? fromJson(dynamic value) {
if (value is Map) {
final json = value.cast<String, dynamic>();
// Ensure that the map contains the required keys.
// Note 1: the values aren't checked for validity beyond being non-null.
// Note 2: this code is stripped in release mode!
assert(() {
requiredKeys.forEach((key) {
assert(json.containsKey(key), 'Required key "QueueStatusDto[$key]" is missing from JSON.');
assert(json[key] != null, 'Required key "QueueStatusDto[$key]" has a null value in JSON.');
});
return true;
}());
return QueueStatusDto(
isActive: mapValueOfType<bool>(json, r'isActive')!,
isPaused: mapValueOfType<bool>(json, r'isPaused')!,
);
}
return null;
}
static List<QueueStatusDto>? listFromJson(dynamic json, {bool growable = false,}) {
final result = <QueueStatusDto>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = QueueStatusDto.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
static Map<String, QueueStatusDto> mapFromJson(dynamic json) {
final map = <String, QueueStatusDto>{};
if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) {
final value = QueueStatusDto.fromJson(entry.value);
if (value != null) {
map[entry.key] = value;
}
}
}
return map;
}
// maps a json object with a list of QueueStatusDto-objects as value to a dart map
static Map<String, List<QueueStatusDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<QueueStatusDto>>{};
if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) {
final value = QueueStatusDto.listFromJson(entry.value, growable: growable,);
if (value != null) {
map[entry.key] = value;
}
}
}
return map;
}
/// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{
'isActive',
'isPaused',
};
}

View File

@@ -16,42 +16,42 @@ void main() {
// final instance = AllJobStatusResponseDto();
group('test AllJobStatusResponseDto', () {
// JobCountsDto thumbnailGenerationQueue
// JobStatusDto thumbnailGenerationQueue
test('to test the property `thumbnailGenerationQueue`', () async {
// TODO
});
// JobCountsDto metadataExtractionQueue
// JobStatusDto metadataExtractionQueue
test('to test the property `metadataExtractionQueue`', () async {
// TODO
});
// JobCountsDto videoConversionQueue
// JobStatusDto videoConversionQueue
test('to test the property `videoConversionQueue`', () async {
// TODO
});
// JobCountsDto objectTaggingQueue
// JobStatusDto objectTaggingQueue
test('to test the property `objectTaggingQueue`', () async {
// TODO
});
// JobCountsDto clipEncodingQueue
// JobStatusDto clipEncodingQueue
test('to test the property `clipEncodingQueue`', () async {
// TODO
});
// JobCountsDto storageTemplateMigrationQueue
// JobStatusDto storageTemplateMigrationQueue
test('to test the property `storageTemplateMigrationQueue`', () async {
// TODO
});
// JobCountsDto backgroundTaskQueue
// JobStatusDto backgroundTaskQueue
test('to test the property `backgroundTaskQueue`', () async {
// TODO
});
// JobCountsDto searchQueue
// JobStatusDto searchQueue
test('to test the property `searchQueue`', () async {
// TODO
});

View File

@@ -26,7 +26,7 @@ void main() {
//
//
//Future sendJobCommand(JobName jobId, JobCommandDto jobCommandDto) async
//Future<JobStatusDto> sendJobCommand(JobName jobId, JobCommandDto jobCommandDto) async
test('test sendJobCommand', () async {
// TODO
});

View File

@@ -0,0 +1,32 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.12
// ignore_for_file: unused_element, unused_import
// ignore_for_file: always_put_required_named_parameters_first
// ignore_for_file: constant_identifier_names
// ignore_for_file: lines_longer_than_80_chars
import 'package:openapi/api.dart';
import 'package:test/test.dart';
// tests for JobStatusDto
void main() {
// final instance = JobStatusDto();
group('test JobStatusDto', () {
// JobCountsDto jobCounts
test('to test the property `jobCounts`', () async {
// TODO
});
// QueueStatusDto queueStatus
test('to test the property `queueStatus`', () async {
// TODO
});
});
}

View File

@@ -0,0 +1,32 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.12
// ignore_for_file: unused_element, unused_import
// ignore_for_file: always_put_required_named_parameters_first
// ignore_for_file: constant_identifier_names
// ignore_for_file: lines_longer_than_80_chars
import 'package:openapi/api.dart';
import 'package:test/test.dart';
// tests for QueueStatusDto
void main() {
// final instance = QueueStatusDto();
group('test QueueStatusDto', () {
// bool isActive
test('to test the property `isActive`', () async {
// TODO
});
// bool isPaused
test('to test the property `isPaused`', () async {
// TODO
});
});
}