diff --git a/src/main/java/com/kingsrook/qbits/collaboration/app/CollaborationHome.java b/src/main/java/com/kingsrook/qbits/collaboration/app/CollaborationHome.java index 8220aec..3564ce4 100644 --- a/src/main/java/com/kingsrook/qbits/collaboration/app/CollaborationHome.java +++ b/src/main/java/com/kingsrook/qbits/collaboration/app/CollaborationHome.java @@ -200,7 +200,7 @@ public final class CollaborationHome row.put("records", tag.getValueInteger("usageCount") == null ? 0 : tag.getValueInteger("usageCount")); rows.add(row); } - return (table("Tags", NO_TAGS, rows, column("Label", "label", "3fr", "left"), column("Table", "table", "2fr", "left"), column("Records", "records", "1fr", "right"))); + return (table("Tags", NO_TAGS, rows, column("Label", "label", "left"), column("Table", "table", "left"), column("Records", "records", "right"))); } @@ -224,8 +224,8 @@ public final class CollaborationHome row.put("indexState", friendly(definition.getValueString("indexState"))); rows.add(row); } - return (table("Custom fields", NO_FIELDS, rows, column("Name", "name", "3fr", "left"), column("Table", "table", "2fr", "left"), - column("Status", "status", "1fr", "left"), column("Index state", "indexState", "1fr", "left"))); + return (table("Custom fields", NO_FIELDS, rows, column("Name", "name", "left"), column("Table", "table", "left"), + column("Status", "status", "left"), column("Index state", "indexState", "left"))); } @@ -249,7 +249,7 @@ public final class CollaborationHome rows.add(row); }); } - return (table("Who's here", admin ? NOBODY_HERE : PRESENCE_ADMIN_ONLY, rows, column("Table", "table", "3fr", "left"), column("People", "viewers", "1fr", "right"))); + return (table("Who's here", admin ? NOBODY_HERE : PRESENCE_ADMIN_ONLY, rows, column("Table", "table", "left"), column("People", "viewers", "right"))); } @@ -267,9 +267,12 @@ public final class CollaborationHome /******************************************************************************* ** A plain column; the frontend styles it. *******************************************************************************/ - private static TableData.Column column(String header, String accessor, String width, String align) + private static TableData.Column column(String header, String accessor, String align) { - return (new TableData.Column("default", header, accessor, width, align)); + ////////////////////////////////////////////////////////////////// + // no width: frontends size the columns to fit narrow (390 px) cards // + ////////////////////////////////////////////////////////////////// + return (new TableData.Column("default", header, accessor, null, align)); } diff --git a/src/main/java/com/kingsrook/qbits/collaboration/attachments/DeleteAttachmentsProcess.java b/src/main/java/com/kingsrook/qbits/collaboration/attachments/DeleteAttachmentsProcess.java index 118868a..639875c 100644 --- a/src/main/java/com/kingsrook/qbits/collaboration/attachments/DeleteAttachmentsProcess.java +++ b/src/main/java/com/kingsrook/qbits/collaboration/attachments/DeleteAttachmentsProcess.java @@ -92,7 +92,7 @@ public final class DeleteAttachmentsProcess *******************************************************************************/ public static class Review extends AbstractTransformStep implements ProcessSummaryProviderInterface { - private final AttachmentsService service; + private AttachmentsService service; private final ProcessSummaryLine selected = line(Status.OK, "will be deleted", "deleted"); private final ProcessSummaryLine skipped = line(Status.WARNING, "cannot be deleted (skipped)", "skipped"); @@ -101,7 +101,11 @@ public final class DeleteAttachmentsProcess *******************************************************************************/ public Review() { - this(new AttachmentsService()); + ////////////////////////////////////////////////////////////////////////// + // no QContext here: QQQ constructs steps during context-free instance // + // validation, so the service is created on first use (in a run) // + ////////////////////////////////////////////////////////////////////////// + this(null); } /******************************************************************************* @@ -112,6 +116,20 @@ public final class DeleteAttachmentsProcess this.service = service; } + + + /******************************************************************************* + ** The attachments service, created on first use inside a run (QContext exists). + *******************************************************************************/ + private AttachmentsService service() + { + if(service == null) + { + service = new AttachmentsService(); + } + return (service); + } + /******************************************************************************* ** Dry run only; no object or row mutations. *******************************************************************************/ @@ -122,7 +140,7 @@ public final class DeleteAttachmentsProcess { try { - QRecord checked = service.requireMutable(row.getValue("id")); + QRecord checked = service().requireMutable(row.getValue("id")); output.getRecords().add(checked); selected.incrementCountAndAddPrimaryKey(checked.getValue("id")); } @@ -151,7 +169,7 @@ public final class DeleteAttachmentsProcess *******************************************************************************/ public static class Delete extends AbstractLoadStep implements ProcessSummaryProviderInterface { - private final AttachmentsService service; + private AttachmentsService service; private final ProcessSummaryLine deleted = line(Status.OK, "will be deleted", "deleted"); private final ProcessSummaryLine failed = line(Status.ERROR, "could not be deleted", "could not be deleted (retry required)"); @@ -160,7 +178,11 @@ public final class DeleteAttachmentsProcess *******************************************************************************/ public Delete() { - this(new AttachmentsService()); + ////////////////////////////////////////////////////////////////////////// + // no QContext here: QQQ constructs steps during context-free instance // + // validation, so the service is created on first use (in a run) // + ////////////////////////////////////////////////////////////////////////// + this(null); } /******************************************************************************* @@ -171,6 +193,20 @@ public final class DeleteAttachmentsProcess this.service = service; } + + + /******************************************************************************* + ** The attachments service, created on first use inside a run (QContext exists). + *******************************************************************************/ + private AttachmentsService service() + { + if(service == null) + { + service = new AttachmentsService(); + } + return (service); + } + /******************************************************************************* ** Count success only after both storage and row deletion succeed. *******************************************************************************/ @@ -181,7 +217,7 @@ public final class DeleteAttachmentsProcess { try { - service.delete(List.of(row.getValue("id"))); + service().delete(List.of(row.getValue("id"))); deleted.incrementCountAndAddPrimaryKey(row.getValue("id")); } catch(QException | RuntimeException expected) diff --git a/src/test/java/com/kingsrook/qbits/collaboration/CollaborationTest.java b/src/test/java/com/kingsrook/qbits/collaboration/CollaborationTest.java index 8eb8b8c..a029f6b 100644 --- a/src/test/java/com/kingsrook/qbits/collaboration/CollaborationTest.java +++ b/src/test/java/com/kingsrook/qbits/collaboration/CollaborationTest.java @@ -228,6 +228,21 @@ class CollaborationTest + /******************************************************************************* + ** Home tables leave column widths to the frontend, so narrow cards wrap + ** instead of scrolling sideways (issue #21). + *******************************************************************************/ + @Test + void homeTablesHaveNoFixedColumnWidths() throws Exception + { + for(TableData table : java.util.List.of(CollaborationHome.tags(), CollaborationHome.fields(), CollaborationHome.presence())) + { + assertThat(table.getColumns()).isNotEmpty().allSatisfy(column -> assertThat(column.getWidth()).isNull()); + } + } + + + /******************************************************************************* ** Empty states explain what has not been populated. *******************************************************************************/ diff --git a/src/test/java/com/kingsrook/qbits/collaboration/attachments/AttachmentsServiceTest.java b/src/test/java/com/kingsrook/qbits/collaboration/attachments/AttachmentsServiceTest.java index 653a0d2..1b0d7f1 100644 --- a/src/test/java/com/kingsrook/qbits/collaboration/attachments/AttachmentsServiceTest.java +++ b/src/test/java/com/kingsrook/qbits/collaboration/attachments/AttachmentsServiceTest.java @@ -102,6 +102,20 @@ public class AttachmentsServiceTest extends PersonalServiceFixture /******************************************************************************* ** Upload, metadata, newest-first list, signed download and deletion round trip. *******************************************************************************/ + /******************************************************************************* + ** The action steps can be constructed without a QContext, as QQQ does during + ** instance validation (issue #20); the service is created on first use. + *******************************************************************************/ + @Test + void stepsConstructWithoutContext() + { + QContext.clear(); + assertThat(new DeleteAttachmentsProcess.Review()).isNotNull(); + assertThat(new DeleteAttachmentsProcess.Delete()).isNotNull(); + } + + + @Test void roundTrip() throws Exception {