Skip to content

Commit

Permalink
Implement advanced options in cloud setup
Browse files Browse the repository at this point in the history
  • Loading branch information
NoelDeMartin committed Aug 15, 2024
1 parent 0077c9a commit 86d8cf9
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 40 deletions.
30 changes: 18 additions & 12 deletions cypress/e2e/cloud.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,18 +167,18 @@ describe('Cloud', () => {

it('Migrates complex local data', () => {
// Arrange
cy.intercept('PUT', podUrl('/tasks/main/')).as('createMainContainer');
cy.intercept('PUT', podUrl('/tasks/household/')).as('createHouseholdContainer');
cy.intercept('PUT', podUrl('/tasks/household/groceries/')).as('createGroceriesContainer');
cy.intercept('PUT', podUrl('/tasks/household/recipes/')).as('createRecipesContainer');
cy.intercept('PUT', podUrl('/tasks/japanese/')).as('createJapaneseContainer');
cy.intercept('PUT', podUrl('/tasks/japanese/manga/')).as('createMangaContainer');
cy.intercept('PATCH', podUrl('/tasks/main/*')).as('createMainTask');
cy.intercept('PATCH', podUrl('/tasks/household/*')).as('createHouseholdTask');
cy.intercept('PATCH', podUrl('/tasks/household/groceries/*')).as('createGroceriesTask');
cy.intercept('PATCH', podUrl('/tasks/household/recipes/*')).as('createRecipesTask');
cy.intercept('PATCH', podUrl('/tasks/japanese/*')).as('createJapaneseTask');
cy.intercept('PATCH', podUrl('/tasks/japanese/manga/*')).as('createMangaTask');
cy.intercept('PUT', podUrl('/tasks/')).as('createMainContainer');
cy.intercept('PUT', podUrl('/shared/')).as('createHouseholdContainer');
cy.intercept('PUT', podUrl('/shared/groceries/')).as('createGroceriesContainer');
cy.intercept('PUT', podUrl('/shared/recipes/')).as('createRecipesContainer');
cy.intercept('PUT', podUrl('/languages/japanese/')).as('createJapaneseContainer');
cy.intercept('PUT', podUrl('/languages/japanese/manga/')).as('createMangaContainer');
cy.intercept('PATCH', podUrl('/tasks/*')).as('createMainTask');
cy.intercept('PATCH', podUrl('/shared/*')).as('createHouseholdTask');
cy.intercept('PATCH', podUrl('/shared/groceries/*')).as('createGroceriesTask');
cy.intercept('PATCH', podUrl('/shared/recipes/*')).as('createRecipesTask');
cy.intercept('PATCH', podUrl('/languages/japanese/*')).as('createJapaneseTask');
cy.intercept('PATCH', podUrl('/languages/japanese/manga/*')).as('createMangaTask');

cy.createStubs();
cy.ariaLabel('Configuration').click();
Expand All @@ -188,10 +188,16 @@ describe('Cloud', () => {

// Act
cy.see('All your eggs are in the same basket');
cy.press('Advanced options', 'summary');
cy.ariaInput('Household url').scrollIntoView().clear().type(podUrl('/shared/'));
cy.ariaInput('Japanese url').scrollIntoView().clear().type(podUrl('/languages/japanese/'));
cy.ariaInput('Main url').scrollIntoView().clear().type(podUrl('/tasks/'));
cy.press('Back up');
cy.dontSee('Loading...', { timeout: 30000 });

// Assert
cy.url().should('equal', `${Cypress.config('baseUrl')}/shared`);

cy.get('@createMainContainer.all').should('have.length', 1);
cy.get('@createHouseholdContainer.all').should('have.length', 1);
cy.get('@createGroceriesContainer.all').should('have.length', 1);
Expand Down
16 changes: 8 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@
"dependencies": {
"@aerogel/core": "0.0.0-next.b243de4e2590f02709edeebd8c13b74087592c04",
"@aerogel/plugin-i18n": "0.0.0-next.13b88f634a31caf3669d61e53e7d3fa95e20488c",
"@aerogel/plugin-offline-first": "0.0.0-next.d91fa1b09d159896747a54add57983993ddb97c4",
"@aerogel/plugin-offline-first": "0.0.0-next.6d7df1c1cb78d86711146139541b17f24d22f4dd",
"@aerogel/plugin-routing": "0.0.0-next.24ab8be1a6189bf1f7eb73dd37b8d21e26a33d72",
"@aerogel/plugin-solid": "0.0.1-next.b243de4e2590f02709edeebd8c13b74087592c04",
"@aerogel/plugin-solid": "0.0.1-next.6d7df1c1cb78d86711146139541b17f24d22f4dd",
"@aerogel/plugin-soukai": "0.0.0-next.d91fa1b09d159896747a54add57983993ddb97c4",
"@headlessui/vue": "^1.7.22",
"@intlify/unplugin-vue-i18n": "^0.12.2",
Expand Down
2 changes: 2 additions & 0 deletions src/lang/en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ cloud:
message: Your data is only stored locally, you should upload it to the cloud to keep it safe. Do you want to proceed backing it up at `{domain}`?
dismiss: not yet
submit: Back up now
advanced: Use the inputs below to configure the containers where each workspace will be created.
workspaceUrl: '{name} url'
info:
setup: Your data is not backed up yet.
changes: 'Everything is up to date. | There is one local change. | There are {n} local changes.'
Expand Down
55 changes: 51 additions & 4 deletions src/pages/workspace/WorkspaceCloudSetup.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<div class="flex flex-col items-center p-8 text-center">
<AGForm :form="form" class="flex flex-col items-center p-8 text-center" @submit="submit()">
<img src="@/assets/img/workspaces/setup.avif" class="w-96" alt="">
<h1 class="mt-4 text-3xl font-semibold">
{{ $t('cloud.setup.title') }}
Expand All @@ -9,28 +9,75 @@
:lang-params="{ domain }"
class="mt-2 text-lg font-light text-gray-600"
/>
<AdvancedOptions class="mt-4 w-full max-w-prose text-start">
<AGMarkdown lang-key="cloud.setup.advanced" class="text-gray-600" />
<ul class="mt-2 space-y-2">
<li v-for="(workspace, index) of $workspaces.all" :key="workspace.url">
<TextInput
:name="`workspaces.${index}`"
:label="$t('cloud.setup.workspaceUrl', { name: workspace.name })"
/>
</li>
</ul>
</AdvancedOptions>
<div class="mt-4 flex flex-row-reverse justify-center gap-2">
<TextButton @click="$ui.loading($cloud.setup())">
<TextButton submit>
<i-ic-sharp-cloud-upload class="h-5 w-5" />
<span class="ml-2">{{ $t('cloud.setup.submit') }}</span>
</TextButton>
<TextButton color="secondary" @click="$cloud.dismissSetup()">
{{ $t('cloud.setup.dismiss') }}
</TextButton>
</div>
</div>
</AGForm>
</template>

<script setup lang="ts">
import { Cloud } from '@aerogel/plugin-offline-first';
import { computed } from 'vue';
import { required, urlParse } from '@noeldemartin/utils';
import { Solid } from '@aerogel/plugin-solid';
import { urlParse } from '@noeldemartin/utils';
import { UI, stringInput, useForm } from '@aerogel/core';
import type { SolidModelConstructor } from 'soukai-solid';
import type { FormFieldDefinition } from '@aerogel/core';
import TasksLists from '@/services/TasksLists';
import Workspace from '@/models/Workspace';
import Workspaces from '@/services/Workspaces';
import { mintWorkspaceUrl } from '@/utils/workspaces';
const remoteCollection = Cloud.requireRemoteCollection(Workspace);
const form = useForm(
Workspaces.all.reduce((fields, workspace, index) => {
fields[`workspaces.${index}`] = stringInput(mintWorkspaceUrl(remoteCollection, required(workspace.name)), {
rules: 'required|container_url',
});
return fields;
}, {} as Record<string, FormFieldDefinition>),
);
const domain = computed(() => {
if (!Solid.user) {
return;
}
return urlParse(Solid.user.storageUrls[0])?.['domain'];
});
async function submit() {
const modelUrlMappings = new WeakMap<SolidModelConstructor, Record<string, string>>();
modelUrlMappings.set(
Workspace,
Workspaces.all.reduce((mappings, workspace, index) => {
mappings[workspace.url] = form.getFieldValue(`workspaces.${index}`) as string;
return mappings;
}, {} as Record<string, string>),
);
await UI.loading(Cloud.setup(modelUrlMappings));
TasksLists.current || (await Workspaces.open());
}
</script>
2 changes: 1 addition & 1 deletion src/pages/workspace/modals/CloudStatusModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@
<i-zondicons-refresh class="h-5 w-5" />
<span class="ml-1">{{ $t('cloud.sync') }}</span>
</TextButton>
<TextButton v-else>
<TextButton v-else @click="($cloud.setupDismissed = false), $modal?.close()">
<i-ic-sharp-cloud-upload class="h-5 w-5" />
<span class="ml-1">{{ $t('cloud.setup.submit') }}</span>
</TextButton>
Expand Down
18 changes: 5 additions & 13 deletions src/pages/workspace/modals/WorkspaceSettingsModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
:label="$t('workspaces.url')"
/>
<AdvancedOptions v-if="$cloud.ready" class="mt-4">
<ul v-if="newRemoteWorkspace" class="mb-4 flex flex-col gap-2">
<ul v-if="createsRemote" class="mb-4 flex flex-col gap-2">
<li>
<label class="flex items-center">
<input
Expand Down Expand Up @@ -76,6 +76,7 @@ import { UI, componentRef, objectProp, requiredStringInput, stringInput, useForm
import SelectInputButton from '@/components/forms/SelectInputButton.vue';
import Workspace from '@/models/Workspace';
import Workspaces from '@/services/Workspaces';
import { mintWorkspaceUrl } from '@/utils/workspaces';
import { THEME_COLORS } from '@/utils/colors';
import type { IFloatingModal } from '@/components/modals/FloatingModal';
import type { ThemeColor } from '@/utils/colors';
Expand All @@ -88,13 +89,7 @@ const form = useForm({
color: requiredStringInput(props.workspace?.themeColor ?? 'sky'),
});
const mintUrl = ref(true);
const newRemoteWorkspace = computed(() => {
if (props.workspace || !Cloud.ready) {
return;
}
return new Workspace();
});
const createsRemote = computed(() => !props.workspace && Cloud.ready);
const colors = computed(() =>
Object.entries(THEME_COLORS).map(([name, value]) => ({
name,
Expand Down Expand Up @@ -127,11 +122,8 @@ watchEffect(() => {
$modal.value?.$panel?.$el?.style.setProperty(`--primary-${name}`, value);
});
if (newRemoteWorkspace.value && mintUrl.value) {
newRemoteWorkspace.value.name = form.name;
newRemoteWorkspace.value.mintUrl();
form.url = newRemoteWorkspace.value.url;
if (createsRemote.value && mintUrl.value) {
form.url = mintWorkspaceUrl(form.name);
}
});
</script>
18 changes: 18 additions & 0 deletions src/utils/workspaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { memo } from '@noeldemartin/utils';

import Workspace from '@/models/Workspace';

export function mintWorkspaceUrl(name: string): string;
export function mintWorkspaceUrl(collection: string, name: string): string;
export function mintWorkspaceUrl(collectionOrName: string, name?: string): string {
const workspace = memo('mint-workspace-url', () => new Workspace());
const collection = typeof name === 'string' ? collectionOrName : Workspace.collection;
const oldCollection = Workspace.collection;

Workspace.collection = collection;
workspace.name = name ?? collectionOrName;
workspace.mintUrl();
Workspace.collection = oldCollection;

return workspace.url;
}

0 comments on commit 86d8cf9

Please sign in to comment.