Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Schema 8 #228

Draft
wants to merge 86 commits into
base: next
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
401d3c9
add protocol validation lib
jthrilly Nov 28, 2024
7d273b7
inline protocol-validation lib
jthrilly Nov 29, 2024
3fc3ab0
protocol-validation tests
jthrilly Nov 29, 2024
014a9eb
update snapshots
jthrilly Nov 29, 2024
a9f5434
add todo for missing migration tests
jthrilly Nov 29, 2024
d799027
reorganize schemas and add scripts to package.json
jthrilly Nov 29, 2024
5ffbde1
add test protocol
jthrilly Nov 29, 2024
29bf413
ignore protocol-validation ts errors for now
jthrilly Nov 29, 2024
ae6953b
fix outstanding errors relating to protocol validation lib
jthrilly Dec 2, 2024
5a5dcb9
Merge branch 'next' into schema-8
jthrilly Dec 5, 2024
a12afae
Merge branch 'main' into schema-8
jthrilly Dec 5, 2024
2ff4c0e
update pnpm lock
jthrilly Dec 5, 2024
4d25c15
first pass at geospatial interface
buckhalt Nov 22, 2024
0268f7f
reset filter on reset
buckhalt Nov 22, 2024
5d1b9f6
simulate getting mapbox key and geojsons from the protocol
buckhalt Nov 27, 2024
d6c8fb8
add filter config to protocol
buckhalt Nov 27, 2024
9fbd0fc
working flow to select map areas for nodes
buckhalt Dec 6, 2024
9386f29
add index.tsx
buckhalt Dec 6, 2024
66a0552
rethink stage layout based on sociogram
buckhalt Dec 10, 2024
bc301d7
implement multiple prompts
buckhalt Dec 10, 2024
01d3c7f
reimplement reset
buckhalt Dec 10, 2024
1b26074
add button import
buckhalt Dec 10, 2024
fd1062e
move nodes to prompt box, add dir aware animation
buckhalt Dec 11, 2024
33ed27f
rm selection state and use node attribute instead
buckhalt Dec 11, 2024
40ad4cf
optimize map initialization to prevent rerenders when node changes
buckhalt Dec 11, 2024
bb2dd23
fix prompt rerendering, animation dir, schema, spacing
buckhalt Dec 12, 2024
9e0b183
linting
buckhalt Dec 12, 2024
43b2bd9
reset active index when prompt changes
buckhalt Dec 12, 2024
0f81663
wip cleanup
buckhalt Dec 12, 2024
365b419
get geojson layers, mapbox token from assets
buckhalt Dec 13, 2024
1563e06
use nc primary color seq for map layers
buckhalt Dec 16, 2024
38d657d
refactor schema to make layers props of prompt
buckhalt Dec 16, 2024
d4d4dfd
improve state management and consts
buckhalt Dec 17, 2024
13f42c4
refactor mapbox stuff into custom hook
buckhalt Dec 17, 2024
266def4
improve layers types
buckhalt Dec 17, 2024
5d49cb6
clean up selection and onSelectionChange
buckhalt Dec 17, 2024
a5d8711
fix type issues
buckhalt Dec 18, 2024
edbf769
feature: skip response for node
buckhalt Dec 18, 2024
26335ad
make node smaller
buckhalt Dec 18, 2024
e812e91
refactor for simpler schema
buckhalt Dec 20, 2024
356eee6
fix resetting selection, rename prop for clarity
buckhalt Dec 20, 2024
76f3922
only allow skip if theres no value
buckhalt Dec 20, 2024
8f063e4
rename data prop to dataSource
buckhalt Jan 7, 2025
9f15e9c
schema udpates, relocate button as icon, prompt box ui improvements
buckhalt Jan 14, 2025
53db3bf
wip fill areas on hover
buckhalt Jan 14, 2025
522837e
action button for reset, fix hover
buckhalt Jan 15, 2025
4622169
zoom can be float
buckhalt Jan 15, 2025
5cebd70
sync with next
jthrilly Jan 16, 2025
f6be250
change the way reset is called to bypass TS error
jthrilly Jan 16, 2025
87b983c
update redux, redux toolkit, and typescript errors related to react a…
jthrilly Jan 16, 2025
caa6b41
remove deviceSettings entirely; switch build action to run on node 22
jthrilly Jan 16, 2025
a56bb37
fix errors related to redux upgrade
jthrilly Jan 16, 2025
cd154dd
remove unused utility
jthrilly Jan 16, 2025
b0f5773
re add thunk middleware to drag and drop store
jthrilly Jan 16, 2025
cd77ed1
Merge branch 'schema-8' into geospatial-interface
buckhalt Jan 16, 2025
1e3d78a
fix getting token, schema 8 changes
buckhalt Jan 17, 2025
c882f7b
refactor how mapbox key is selected/used
buckhalt Jan 17, 2025
6d69eaa
a few small improvements to get things building
jthrilly Jan 22, 2025
f5456a5
add schema migration to add value column to asset table
jthrilly Jan 22, 2025
683e340
knip
jthrilly Jan 22, 2025
a18f058
fix test protocol styles
buckhalt Jan 22, 2025
e6631be
implement adding apikey assets to db
buckhalt Jan 22, 2025
f536868
knip
jthrilly Jan 23, 2025
28ede8c
reuse existing types for fetching assets
buckhalt Jan 23, 2025
6bfeae0
add asset types to json schema & compile schemas
buckhalt Jan 23, 2025
8230f08
allow schema versions 7 & 8
buckhalt Jan 23, 2025
90cd778
rm symbol
buckhalt Jan 24, 2025
683049c
add zoom buttons for better ux
buckhalt Jan 24, 2025
2d1b852
feature: introduction panel
buckhalt Jan 24, 2025
4599fe9
fix backwards nav, animation between intro and map
buckhalt Jan 24, 2025
db7da08
fix error related to redux upgrade
buckhalt Jan 27, 2025
b94c2d9
handle existing apikeys as existing assets to fix duplicate key
buckhalt Jan 27, 2025
aeed61e
correct checking for existing api key asset implementation
buckhalt Jan 27, 2025
7fffeec
update migration notes
jthrilly Jan 28, 2025
e405ff4
fix: do not convert to iso string if it already has been converted
buckhalt Jan 28, 2025
de0cea8
handle optional/required dates
buckhalt Jan 28, 2025
c1cc09a
use helpers to check for already converted dates for all dates in int…
buckhalt Jan 28, 2025
9b9d6ff
fix logout on dashboard and feedback banner
buckhalt Jan 28, 2025
c812ac6
replace skip button with outside selectable areas button
buckhalt Jan 28, 2025
f19a5d6
don't automatically move forward on outside selectable areas click
buckhalt Jan 29, 2025
30dfc6d
rm getNavigationHelpers
buckhalt Jan 29, 2025
48f2faa
implement overlay for outside selectable areas
buckhalt Feb 3, 2025
8222e46
use latest corepack for gh build
buckhalt Feb 3, 2025
4b36b40
Merge pull request #236 from complexdatacollective/geospatial-interface
jthrilly Feb 11, 2025
5a3b042
merge with next
jthrilly Feb 11, 2025
d6715e2
replace inlined protocol-validation lib with package
buckhalt Feb 11, 2025
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
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ jobs:
- name: Enable Corepack
run: corepack enable

- name: Use Node 20.x
- name: Use Node 22.x
uses: actions/setup-node@v4
with:
node-version: 20.x
node-version: 22.x
cache: 'pnpm'

- name: Install dependencies
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.WavesAudio.SoundGridStudioSilent</string>
<key>ProgramArguments</key>
<array>
<string>/Applications/Waves/SoundGrid Studio/SoundGrid Studio.app/Contents/MacOS/SoundGrid Studio</string>
<string>--NoSplashScreen</string>
<string>--Minimized</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
4 changes: 2 additions & 2 deletions app/(blobs)/expired/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ export default async function Page() {
Please redeploy a new instance of Fresco to continue using the software.
</p>
{env.NODE_ENV === 'development' && (
<form action={resetAppSettings}>
<SubmitButton className="mt-6 max-w-[20rem]" type="submit">
<form action={void resetAppSettings}>
<SubmitButton className="mt-6 max-w-[20rem]">
Dev mode: Reset Configuration
</SubmitButton>
</form>
Expand Down
45 changes: 39 additions & 6 deletions app/(interview)/interview/_components/InterviewShell.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
'use client';

import { parseAsInteger, useQueryState } from 'nuqs';
import { useEffect, useState } from 'react';
import { Provider } from 'react-redux';
import type { SyncInterviewType } from '~/actions/interviews';
import DialogManager from '~/lib/interviewer/components/DialogManager';
import ProtocolScreen from '~/lib/interviewer/containers/ProtocolScreen';
import {
SET_SERVER_SESSION,
type SetServerSessionAction,
} from '~/lib/interviewer/ducks/modules/setServerSession';
import { store } from '~/lib/interviewer/store';
import ServerSync from './ServerSync';
import { useEffect, useState } from 'react';
import { parseAsInteger, useQueryState } from 'nuqs';
import type { SyncInterviewType } from '~/actions/interviews';
import type { getInterviewById } from '~/queries/interviews';
import ServerSync from './ServerSync';

// The job of interview shell is to receive the server-side session and protocol
// and create a redux store with that data.
Expand Down Expand Up @@ -45,12 +45,45 @@ const InterviewShell = ({
serverSession.currentStep = currentStage;
}

// You can't store dates in the redux store, so we need to convert them.
// If the date is already an ISOstring, we don't need to convert it.

const toRequiredISOString = (date: string | Date): string => {
if (date && date instanceof Date) {
return date.toISOString();
}
return date;
};

const toOptionalISOString = (date: string | Date | null): string | null => {
if (!date) return null;
if (date instanceof Date) {
return date.toISOString();
}
return date;
};

const serialisableServerSession = {
...serverSession,
startTime: toRequiredISOString(serverSession.startTime),
lastUpdated: toRequiredISOString(serverSession.lastUpdated),
// optional - can be null
finishTime: toOptionalISOString(serverSession.finishTime),
exportTime: toOptionalISOString(serverSession.exportTime),
};

const serialisableProtocol = {
...protocol,
importedAt: toRequiredISOString(protocol.importedAt),
lastModified: toRequiredISOString(protocol.lastModified),
};

// If there's no current stage in the URL bar, set it.
store.dispatch<SetServerSessionAction>({
type: SET_SERVER_SESSION,
payload: {
protocol,
session: serverSession,
protocol: serialisableProtocol,
session: serialisableServerSession,
},
});

Expand Down
4 changes: 2 additions & 2 deletions app/dashboard/_components/UserMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import SubmitButton from '~/components/ui/SubmitButton';

const UserMenu = () => {
return (
<form action={() => logout()}>
<SubmitButton variant="secondary" size="sm" type="submit">
<form action={() => void logout()}>
<SubmitButton variant="secondary" size="sm" type="submit" onClick={() => logout()}>
Sign out
</SubmitButton>
</form>
Expand Down
4 changes: 2 additions & 2 deletions components/Feedback/SignOutModal.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client';

import { type Dispatch, type SetStateAction } from 'react';
import { logout } from '~/actions/auth';
import {
AlertDialog,
AlertDialogAction,
Expand All @@ -11,7 +12,6 @@ import {
AlertDialogHeader,
AlertDialogTitle,
} from '../ui/AlertDialog';
import { logout } from '~/actions/auth';

type SignOutModalProps = {
openSignOutModal: boolean;
Expand Down Expand Up @@ -39,7 +39,7 @@ const SignOutModal = ({
<AlertDialogCancel onClick={() => setOpenSignOutModal(false)}>
Cancel
</AlertDialogCancel>
<AlertDialogAction onClick={() => void logout()}>
<AlertDialogAction onClick={() => logout()}>
Sign Out and Hide Banner
</AlertDialogAction>
</AlertDialogFooter>
Expand Down
3 changes: 2 additions & 1 deletion fresco.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export const PROTOCOL_EXTENSION = '.netcanvas';
export const APP_SUPPORTED_SCHEMA_VERSIONS = [7];
export const APP_SUPPORTED_SCHEMA_VERSIONS = [7, 8];

// If unconfigured, the app will shut down after 2 hours (7200000 ms)
export const UNCONFIGURED_TIMEOUT = 7200000;

Expand Down
45 changes: 29 additions & 16 deletions hooks/useProtocolImport.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { validateProtocol } from '@codaco/protocol-validation';
import { queue } from 'async';
import { XCircle } from 'lucide-react';
import { hash } from 'ohash';
Expand All @@ -12,7 +13,7 @@ import {
import { AlertDialogDescription } from '~/components/ui/AlertDialog';
import { APP_SUPPORTED_SCHEMA_VERSIONS } from '~/fresco.config';
import { uploadFiles } from '~/lib/uploadthing-client-helpers';
import { getExistingAssetIds, getProtocolByHash } from '~/queries/protocols';
import { getNewAssetIds, getProtocolByHash } from '~/queries/protocols';
import { type AssetInsertType } from '~/schemas/protocol';
import { DatabaseError } from '~/utils/databaseError';
import { ensureError } from '~/utils/ensureError';
Expand Down Expand Up @@ -87,8 +88,6 @@ export const useProtocolImport = () => {
return;
}

const { validateProtocol } = await import('@codaco/protocol-validation');

const validationResult = await validateProtocol(protocolJson);

if (!validationResult.isValid) {
Expand Down Expand Up @@ -130,7 +129,7 @@ export const useProtocolImport = () => {
...validationResult.logicErrors,
].map((validationError, i) => (
<li className="flex capitalize" key={i}>
<XCircle className="mr-2 h-4 w-4 text-destructive" />
<XCircle className="text-destructive mr-2 h-4 w-4" />
<span>
{validationError.message}{' '}
<span className="text-xs italic">
Expand Down Expand Up @@ -176,30 +175,44 @@ export const useProtocolImport = () => {
return;
}

const assets = await getProtocolAssets(protocolJson, zip);

const newAssets: typeof assets = [];
const { fileAssets, apikeyAssets } = await getProtocolAssets(
protocolJson,
zip,
);

const newAssets: typeof fileAssets = [];
const existingAssetIds: string[] = [];

let newAssetsWithCombinedMetadata: AssetInsertType = [];
let newAssetsWithCombinedMetadata: AssetInsertType[] = [];
const newApikeyAssets: typeof apikeyAssets = [];

// Check if the assets are already in the database.
// If yes, add them to existingAssetIds to be connected to the protocol.
// If not, add them to newAssets to be uploaded.

// If not, add files to newAssets to be uploaded
// and add apikeys to newApikeyAssets to be created in the database with the protocol
try {
const newAssetIds = await getExistingAssetIds(
assets.map((asset) => asset.assetId),
const newFileAssetIds = await getNewAssetIds(
fileAssets.map((asset) => asset.assetId),
);

assets.forEach((asset) => {
if (newAssetIds.includes(asset.assetId)) {
fileAssets.forEach((asset) => {
if (newFileAssetIds.includes(asset.assetId)) {
newAssets.push(asset);
} else {
existingAssetIds.push(asset.assetId);
}
});

const newApikeyAssetIds = await getNewAssetIds(
apikeyAssets.map((apiKey) => apiKey.assetId),
);

apikeyAssets.forEach((apiKey) => {
if (newApikeyAssetIds.includes(apiKey.assetId)) {
newApikeyAssets.push(apiKey);
} else {
existingAssetIds.push(apiKey.assetId);
}
});
} catch (e) {
throw new Error('Error checking for existing assets');
}
Expand Down Expand Up @@ -304,7 +317,7 @@ export const useProtocolImport = () => {
const result = await insertProtocol({
protocol: protocolJson,
protocolName: fileName,
newAssets: newAssetsWithCombinedMetadata,
newAssets: [...newAssetsWithCombinedMetadata, ...newApikeyAssets],
existingAssetIds: existingAssetIds,
});

Expand Down
2 changes: 1 addition & 1 deletion lib/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ type StaticTag =
| 'getProtocols'
| 'getInterviewsForExport'
| 'getProtocolsByHash'
| 'getExistingAssetIds'
| 'getNewAssetIds'
| 'interviewCount'
| 'protocolCount'
| 'participantCount';
Expand Down
16 changes: 8 additions & 8 deletions lib/interviewer/behaviours/DragAndDrop/reducer.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { omit } from 'es-toolkit';
import { filter, isEmpty, some } from 'es-toolkit/compat';

const UPSERT_TARGET = Symbol('DRAG_AND_DROP/UPSERT_TARGET');
const RENAME_TARGET = Symbol('DRAG_AND_DROP/RENAME_TARGET');
const REMOVE_TARGET = Symbol('DRAG_AND_DROP/REMOVE_TARGET');
const UPSERT_OBSTACLE = Symbol('DRAG_AND_DROP/UPSERT_OBSTACLE');
const REMOVE_OBSTACLE = Symbol('DRAG_AND_DROP/REMOVE_OBSTACLE');
const DRAG_START = Symbol('DRAG_AND_DROP/DRAG_START');
const DRAG_MOVE = Symbol('DRAG_AND_DROP/DRAG_MOVE');
const DRAG_END = Symbol('DRAG_AND_DROP/DRAG_END');
const UPSERT_TARGET = 'DRAG_AND_DROP/UPSERT_TARGET';
const RENAME_TARGET = 'DRAG_AND_DROP/RENAME_TARGET';
const REMOVE_TARGET = 'DRAG_AND_DROP/REMOVE_TARGET';
const UPSERT_OBSTACLE = 'DRAG_AND_DROP/UPSERT_OBSTACLE';
const REMOVE_OBSTACLE = 'DRAG_AND_DROP/REMOVE_OBSTACLE';
const DRAG_START = 'DRAG_AND_DROP/DRAG_START';
const DRAG_MOVE = 'DRAG_AND_DROP/DRAG_MOVE';
const DRAG_END = 'DRAG_AND_DROP/DRAG_END';

const initialState = {
targets: [],
Expand Down
10 changes: 7 additions & 3 deletions lib/interviewer/behaviours/DragAndDrop/store.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import thunk from 'redux-thunk';
import reducer from './reducer';
import { configureStore } from '@reduxjs/toolkit';
import reducer from './reducer';

const store = configureStore({
reducer,
middleware: [thunk],
// We need to remove the default middleware to avoid errors related to values that can't be serialized (onDrop functions)
// We need the thunk middleware, though!
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: false,
}),
});

export default store;
4 changes: 3 additions & 1 deletion lib/interviewer/components/CollapsablePrompts.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useEffect, useRef, useState } from 'react';
import Prompts from './Prompts';

const CollapsablePrompts = (props) => {
const { currentPromptIndex, dragConstraints } = props;
const { currentPromptIndex, dragConstraints, children } = props;
const ref = useRef(null);
const [minimized, setMinimized] = useState(false);

Expand Down Expand Up @@ -66,8 +66,10 @@ const CollapsablePrompts = (props) => {
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="flex flex-col items-center"
>
<Prompts />
{children}
</motion.div>
)}
</AnimatePresence>
Expand Down
10 changes: 4 additions & 6 deletions lib/interviewer/components/Node.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { useSelector } from 'react-redux';
import { getEntityAttributes } from '~/lib/interviewer/ducks/modules/network';
import UINode from '~/lib/ui/components/Node';
import {
getNodeColor, labelLogic,
} from '../selectors/network';
import { getNodeColor, labelLogic } from '../selectors/network';
import { getProtocolCodebook } from '../selectors/protocol';
import { getEntityAttributes } from '~/lib/interviewer/ducks/modules/network';

/**
* Renders a Node.
*/
* Renders a Node.
*/

const Node = (props) => {
const { type } = props;
Expand Down
5 changes: 1 addition & 4 deletions lib/interviewer/components/Prompts.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import React from 'react';
import UIPrompts from '~/lib/ui/components/Prompts/Prompts';
import { useSelector } from 'react-redux';
import { usePrompts } from '../behaviours/withPrompt';

const Prompts = () => {
const { prompt, prompts } = usePrompts();
const speakable = useSelector((state) => state.deviceSettings.enableExperimentalTTS);

return <UIPrompts speakable={speakable} currentPromptId={prompt.id} prompts={prompts} />;
return <UIPrompts currentPromptId={prompt.id} prompts={prompts} />;
};

export default Prompts;
1 change: 0 additions & 1 deletion lib/interviewer/containers/Interfaces/DyadCensus/index.js

This file was deleted.

Loading
Loading