Skip to content

Commit cde927c

Browse files
server: fix school data files endpoints
1 parent 4ccf255 commit cde927c

6 files changed

Lines changed: 281 additions & 27 deletions

File tree

school_data_hub_client/lib/src/protocol/client.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,22 @@ class EndpointAdminSchoolData extends _i1.EndpointRef {
236236
'createdBy': createdBy,
237237
},
238238
);
239+
240+
/// Delete the logo from SchoolData
241+
_i2.Future<_i8.SchoolData> deleteLogo(int schoolDataId) =>
242+
caller.callServerEndpoint<_i8.SchoolData>(
243+
'adminSchoolData',
244+
'deleteLogo',
245+
{'schoolDataId': schoolDataId},
246+
);
247+
248+
/// Delete the official seal from SchoolData
249+
_i2.Future<_i8.SchoolData> deleteOfficialSeal(int schoolDataId) =>
250+
caller.callServerEndpoint<_i8.SchoolData>(
251+
'adminSchoolData',
252+
'deleteOfficialSeal',
253+
{'schoolDataId': schoolDataId},
254+
);
239255
}
240256

241257
/// {@category Endpoint}

school_data_hub_flutter/lib/features/server_logs/presentation/widgets/session_log_card.dart

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import 'package:flutter/material.dart';
2+
import 'package:flutter/services.dart';
3+
import 'package:gap/gap.dart';
24
import 'package:school_data_hub_client/school_data_hub_client.dart';
35
import 'package:school_data_hub_flutter/common/theme/app_colors.dart';
46
import 'package:school_data_hub_flutter/common/theme/styles.dart';
@@ -32,6 +34,18 @@ class SessionLogCard extends StatelessWidget {
3234
? '${entry.duration!.toStringAsFixed(0)} ms'
3335
: 'offen';
3436

37+
void copyToClipboard() {
38+
Clipboard.setData(
39+
ClipboardData(text: info.sessionLogEntry.error ?? 'kein Inhalt'),
40+
);
41+
ScaffoldMessenger.of(context).showSnackBar(
42+
const SnackBar(
43+
content: Text('In Zwischenablage kopiert'),
44+
duration: Duration(seconds: 2),
45+
),
46+
);
47+
}
48+
3549
return Card(
3650
color: AppColors.cardInCardColor,
3751
shape: RoundedRectangleBorder(
@@ -90,6 +104,14 @@ class SessionLogCard extends StatelessWidget {
90104
'${entry.numQueries} Q',
91105
style: AppStyles.textLabel.copyWith(color: Colors.black54),
92106
),
107+
const Gap(20),
108+
IconButton(
109+
icon: const Icon(Icons.copy, size: 20),
110+
padding: EdgeInsets.zero,
111+
constraints: const BoxConstraints(),
112+
onPressed: copyToClipboard,
113+
tooltip: 'Kopieren',
114+
),
93115
],
94116
],
95117
),
@@ -148,17 +170,24 @@ class _ExpandableMonoTextState extends State<_ExpandableMonoText> {
148170

149171
@override
150172
Widget build(BuildContext context) {
151-
return GestureDetector(
152-
onTap: () => setState(() => _expanded = !_expanded),
153-
child: SelectableText(
154-
widget.text,
155-
maxLines: _expanded ? null : 2,
156-
style: TextStyle(
157-
fontFamily: 'monospace',
158-
fontSize: 12,
159-
color: widget.color,
173+
return Row(
174+
crossAxisAlignment: CrossAxisAlignment.start,
175+
children: [
176+
Expanded(
177+
child: GestureDetector(
178+
onTap: () => setState(() => _expanded = !_expanded),
179+
child: Text(
180+
widget.text,
181+
maxLines: _expanded ? null : 2,
182+
style: TextStyle(
183+
fontFamily: 'monospace',
184+
fontSize: 12,
185+
color: widget.color,
186+
),
187+
),
188+
),
160189
),
161-
),
190+
],
162191
);
163192
}
164193
}

school_data_hub_server/lib/src/_features/admin/endpoints/admin_school_data_endpoint.dart

Lines changed: 128 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,8 @@ class AdminSchoolDataEndpoint extends Endpoint {
6161
throw Exception('SchoolData not found');
6262
}
6363

64-
// Delete old logo if exists
65-
if (schoolData.logoId != null && schoolData.logo != null) {
66-
await HubDocumentHelper().deleteHubDocumentAndFile(
67-
session: session,
68-
documentId: schoolData.logo!.documentId,
69-
transaction: transaction,
70-
);
71-
}
64+
// Store old logo document for deletion
65+
final oldLogoDocument = schoolData.logo;
7266

7367
// Create new HubDocument for the logo
7468
final document = HubDocumentHelper().createHubDocumentObject(
@@ -92,6 +86,15 @@ class AdminSchoolDataEndpoint extends Endpoint {
9286
transaction: transaction,
9387
);
9488

89+
// Delete old logo if exists (after updating the foreign key)
90+
if (oldLogoDocument != null) {
91+
await HubDocumentHelper().deleteHubDocumentAndFile(
92+
session: session,
93+
documentId: oldLogoDocument.documentId,
94+
transaction: transaction,
95+
);
96+
}
97+
9598
// Return the updated SchoolData with includes
9699
return await SchoolData.db.findById(
97100
session,
@@ -125,15 +128,8 @@ class AdminSchoolDataEndpoint extends Endpoint {
125128
throw Exception('SchoolData not found');
126129
}
127130

128-
// Delete old seal if exists
129-
if (schoolData.officialSealId != null &&
130-
schoolData.officialSeal != null) {
131-
await HubDocumentHelper().deleteHubDocumentAndFile(
132-
session: session,
133-
documentId: schoolData.officialSeal!.documentId,
134-
transaction: transaction,
135-
);
136-
}
131+
// Store old seal document for deletion
132+
final oldSealDocument = schoolData.officialSeal;
137133

138134
// Create new HubDocument for the seal
139135
final document = HubDocumentHelper().createHubDocumentObject(
@@ -157,6 +153,121 @@ class AdminSchoolDataEndpoint extends Endpoint {
157153
transaction: transaction,
158154
);
159155

156+
// Delete old seal if exists (after updating the foreign key)
157+
if (oldSealDocument != null) {
158+
await HubDocumentHelper().deleteHubDocumentAndFile(
159+
session: session,
160+
documentId: oldSealDocument.documentId,
161+
transaction: transaction,
162+
);
163+
}
164+
165+
// Return the updated SchoolData with includes
166+
return await SchoolData.db.findById(
167+
session,
168+
schoolDataId,
169+
include: SchoolData.include(
170+
logo: HubDocument.include(),
171+
officialSeal: HubDocument.include(),
172+
),
173+
transaction: transaction,
174+
);
175+
});
176+
return result!;
177+
}
178+
179+
/// Delete the logo from SchoolData
180+
Future<SchoolData> deleteLogo(
181+
Session session,
182+
int schoolDataId,
183+
) async {
184+
final result = await session.db.transaction((transaction) async {
185+
// Fetch SchoolData inside transaction
186+
final schoolData = await SchoolData.db.findById(
187+
session,
188+
schoolDataId,
189+
include: SchoolData.include(logo: HubDocument.include()),
190+
transaction: transaction,
191+
);
192+
if (schoolData == null) {
193+
throw Exception('SchoolData not found');
194+
}
195+
196+
// Store logo document for deletion
197+
final logoDocument = schoolData.logo;
198+
199+
// Update SchoolData to remove logoId reference
200+
final updatedSchoolData = schoolData.copyWith(
201+
logoId: null,
202+
);
203+
await SchoolData.db.updateRow(
204+
session,
205+
updatedSchoolData,
206+
transaction: transaction,
207+
);
208+
209+
// Delete logo document and file if exists
210+
if (logoDocument != null) {
211+
await HubDocumentHelper().deleteHubDocumentAndFile(
212+
session: session,
213+
documentId: logoDocument.documentId,
214+
transaction: transaction,
215+
);
216+
}
217+
218+
// Return the updated SchoolData with includes
219+
return await SchoolData.db.findById(
220+
session,
221+
schoolDataId,
222+
include: SchoolData.include(
223+
logo: HubDocument.include(),
224+
officialSeal: HubDocument.include(),
225+
),
226+
transaction: transaction,
227+
);
228+
});
229+
return result!;
230+
}
231+
232+
/// Delete the official seal from SchoolData
233+
Future<SchoolData> deleteOfficialSeal(
234+
Session session,
235+
int schoolDataId,
236+
) async {
237+
final result = await session.db.transaction((transaction) async {
238+
// Fetch SchoolData inside transaction
239+
final schoolData = await SchoolData.db.findById(
240+
session,
241+
schoolDataId,
242+
include: SchoolData.include(officialSeal: HubDocument.include()),
243+
transaction: transaction,
244+
);
245+
if (schoolData == null) {
246+
throw Exception('SchoolData not found');
247+
}
248+
249+
// Store seal document for deletion
250+
final sealDocument = schoolData.officialSeal;
251+
252+
// Update SchoolData to remove officialSealId reference
253+
final updatedSchoolData = schoolData.copyWith(
254+
officialSealId: null,
255+
);
256+
await SchoolData.db.updateRow(
257+
session,
258+
updatedSchoolData,
259+
transaction: transaction,
260+
);
261+
262+
// Delete seal document and file if exists
263+
if (sealDocument != null) {
264+
await HubDocumentHelper().deleteHubDocumentAndFile(
265+
session: session,
266+
documentId: sealDocument.documentId,
267+
transaction: transaction,
268+
);
269+
}
270+
160271
// Return the updated SchoolData with includes
161272
return await SchoolData.db.findById(
162273
session,

school_data_hub_server/lib/src/generated/endpoints.dart

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,44 @@ class Endpoints extends _i1.EndpointDispatch {
595595
params['createdBy'],
596596
),
597597
),
598+
'deleteLogo': _i1.MethodConnector(
599+
name: 'deleteLogo',
600+
params: {
601+
'schoolDataId': _i1.ParameterDescription(
602+
name: 'schoolDataId',
603+
type: _i1.getType<int>(),
604+
nullable: false,
605+
)
606+
},
607+
call: (
608+
_i1.Session session,
609+
Map<String, dynamic> params,
610+
) async =>
611+
(endpoints['adminSchoolData'] as _i5.AdminSchoolDataEndpoint)
612+
.deleteLogo(
613+
session,
614+
params['schoolDataId'],
615+
),
616+
),
617+
'deleteOfficialSeal': _i1.MethodConnector(
618+
name: 'deleteOfficialSeal',
619+
params: {
620+
'schoolDataId': _i1.ParameterDescription(
621+
name: 'schoolDataId',
622+
type: _i1.getType<int>(),
623+
nullable: false,
624+
)
625+
},
626+
call: (
627+
_i1.Session session,
628+
Map<String, dynamic> params,
629+
) async =>
630+
(endpoints['adminSchoolData'] as _i5.AdminSchoolDataEndpoint)
631+
.deleteOfficialSeal(
632+
session,
633+
params['schoolDataId'],
634+
),
635+
),
598636
},
599637
);
600638
connectors['adminSchoolDay'] = _i1.EndpointConnector(

school_data_hub_server/lib/src/generated/protocol.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ adminSchoolData:
1010
- updateSchoolData:
1111
- uploadLogo:
1212
- uploadOfficialSeal:
13+
- deleteLogo:
14+
- deleteOfficialSeal:
1315
adminSchoolDay:
1416
- createSchoolSemester:
1517
- updateSchoolSemester:

school_data_hub_server/test/integration/test_tools/serverpod_test_tools.dart

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,64 @@ class _AdminSchoolDataEndpoint {
753753
}
754754
});
755755
}
756+
757+
_i3.Future<_i9.SchoolData> deleteLogo(
758+
_i1.TestSessionBuilder sessionBuilder,
759+
int schoolDataId,
760+
) async {
761+
return _i1.callAwaitableFunctionAndHandleExceptions(() async {
762+
var _localUniqueSession =
763+
(sessionBuilder as _i1.InternalTestSessionBuilder).internalBuild(
764+
endpoint: 'adminSchoolData',
765+
method: 'deleteLogo',
766+
);
767+
try {
768+
var _localCallContext = await _endpointDispatch.getMethodCallContext(
769+
createSessionCallback: (_) => _localUniqueSession,
770+
endpointPath: 'adminSchoolData',
771+
methodName: 'deleteLogo',
772+
parameters: _i1.testObjectToJson({'schoolDataId': schoolDataId}),
773+
serializationManager: _serializationManager,
774+
);
775+
var _localReturnValue = await (_localCallContext.method.call(
776+
_localUniqueSession,
777+
_localCallContext.arguments,
778+
) as _i3.Future<_i9.SchoolData>);
779+
return _localReturnValue;
780+
} finally {
781+
await _localUniqueSession.close();
782+
}
783+
});
784+
}
785+
786+
_i3.Future<_i9.SchoolData> deleteOfficialSeal(
787+
_i1.TestSessionBuilder sessionBuilder,
788+
int schoolDataId,
789+
) async {
790+
return _i1.callAwaitableFunctionAndHandleExceptions(() async {
791+
var _localUniqueSession =
792+
(sessionBuilder as _i1.InternalTestSessionBuilder).internalBuild(
793+
endpoint: 'adminSchoolData',
794+
method: 'deleteOfficialSeal',
795+
);
796+
try {
797+
var _localCallContext = await _endpointDispatch.getMethodCallContext(
798+
createSessionCallback: (_) => _localUniqueSession,
799+
endpointPath: 'adminSchoolData',
800+
methodName: 'deleteOfficialSeal',
801+
parameters: _i1.testObjectToJson({'schoolDataId': schoolDataId}),
802+
serializationManager: _serializationManager,
803+
);
804+
var _localReturnValue = await (_localCallContext.method.call(
805+
_localUniqueSession,
806+
_localCallContext.arguments,
807+
) as _i3.Future<_i9.SchoolData>);
808+
return _localReturnValue;
809+
} finally {
810+
await _localUniqueSession.close();
811+
}
812+
});
813+
}
756814
}
757815

758816
class _AdminSchoolDayEndpoint {

0 commit comments

Comments
 (0)