Skip to content

Commit

Permalink
Merge branch 'main' into feature/registerbeforenext-oncomplete
Browse files Browse the repository at this point in the history
  • Loading branch information
jthrilly authored Jan 11, 2024
2 parents bc85e36 + a0e4720 commit 3123d8f
Show file tree
Hide file tree
Showing 72 changed files with 1,436 additions and 2,062 deletions.
8 changes: 7 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,11 @@ MAXMIND_ACCOUNT_ID=xxxxxxxx

# optional global override for analytics
# can be used to diable analytics in development
#NEXT_PUBLIC_DISABLE_ANALYTICS=true
#DISABLE_ANALYTICS=true

# optional manual specification for installation ID. Useful in scenarios such as
# CI/CD where the installation ID cannot be automatically determined because
# there is no database. Also useful for ensuring consistent ID between DB
# resets.
#INSTALLATION_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

5 changes: 5 additions & 0 deletions .github/workflows/lint.yml → .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ jobs:
build:
runs-on: ubuntu-latest

env:
SKIP_ENV_VALIDATION: true
DISABLE_ANALYTICS: true
INSTALLATION_ID: gh-action

steps:
- name: Checkout code
uses: actions/checkout@v3
Expand Down
File renamed without changes.
27 changes: 11 additions & 16 deletions analytics/utils.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,19 @@
import { makeEventTracker } from '@codaco/analytics';
import { cache } from 'react';
import { api } from '~/trpc/server';
import { getBaseUrl } from '~/trpc/shared';
import { env } from '~/env.mjs';
import { prisma } from '~/utils/db';

export const getInstallationId = cache(async () => {
const installationId = await api.appSettings.getInstallationId.query();

if (installationId) {
return installationId;
if (env.INSTALLATION_ID) {
return env.INSTALLATION_ID;
}

return 'Unknown';
});
// eslint-disable-next-line local-rules/require-data-mapper
const appSettings = await prisma.appSettings.findFirst();

// eslint-disable-next-line no-process-env
const globalAnalyticsEnabled = process.env.NEXT_PUBLIC_ANALYTICS_ENABLED;
return appSettings?.installationId ?? 'Unknown';
});

export const trackEvent =
globalAnalyticsEnabled !== 'false'
? makeEventTracker({
endpoint: getBaseUrl() + '/api/analytics',
})
: () => {};
export const trackEvent = !env.DISABLE_ANALYTICS
? makeEventTracker()
: () => null;
75 changes: 75 additions & 0 deletions app/(interview)/interview/_components/FinishInterviewModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { type Dispatch, type SetStateAction } from 'react';
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from '~/components/ui/AlertDialog';
import { useRouter } from 'next/navigation';
import { api } from '~/trpc/client';
import { usePathname } from 'next/navigation';
import { clientRevalidateTag } from '~/utils/clientRevalidate';

type FinishInterviewModalProps = {
open: boolean;
setOpen: Dispatch<SetStateAction<boolean>>;
};

const FinishInterviewModal = ({ open, setOpen }: FinishInterviewModalProps) => {
const router = useRouter();
const pathname = usePathname();

const interviewId = pathname.split('/').pop();
const { mutateAsync: finishInterview } = api.interview.finish.useMutation({
onError(error) {
throw new Error(error.message);
},
async onSuccess() {
await clientRevalidateTag('interview.get.byId');

router.push('/interview/finished');
},
});
const handleFinishInterview = async () => {
if (!interviewId) {
throw new Error('No interview id found');
}
await finishInterview({ id: interviewId });
};
return (
<AlertDialog open={open}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle className="text-base">
Are you sure you want finish the interview?
</AlertDialogTitle>
<AlertDialogDescription className="text-sm">
Your responses cannot be changed after you finish the interview.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel
onClick={() => {
setOpen(false);
}}
>
Cancel
</AlertDialogCancel>
<AlertDialogAction
onClick={async () => {
await handleFinishInterview();
}}
>
Finish Interview
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
};

export default FinishInterviewModal;
5 changes: 5 additions & 0 deletions app/(interview)/interview/_components/InterviewShell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
import { getActiveSession } from '~/lib/interviewer/selectors/session';
import { store } from '~/lib/interviewer/store';
import { api } from '~/trpc/client';
import { useRouter } from 'next/navigation';

// The job of ServerSync is to listen to actions in the redux store, and to sync
// data with the server.
Expand Down Expand Up @@ -68,6 +69,7 @@ const ServerSync = ({ interviewId }: { interviewId: string }) => {
// Eventually it will handle syncing this data back.
const InterviewShell = ({ interviewID }: { interviewID: string }) => {
const [currentStage, setCurrentStage] = useQueryState('stage');
const router = useRouter();

const { isLoading } = api.interview.get.byId.useQuery(
{ id: interviewID },
Expand All @@ -79,6 +81,9 @@ const InterviewShell = ({ interviewID }: { interviewID: string }) => {
if (!data) {
return;
}
if (data.finishTime) {
router.push('/interview/finished');
}

const { protocol, ...serverSession } = data;

Expand Down
15 changes: 15 additions & 0 deletions app/(interview)/interview/finished/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { BadgeCheck } from 'lucide-react';

export default function InterviewCompleted() {
return (
<div className="flex h-screen flex-col items-center justify-center bg-[var(--nc-background)]">
<BadgeCheck className="mb-4 h-12 w-12 text-[var(--color-sea-green)]" />
<h1 className="text-3xl font-extrabold text-white">
Thank you for participating!
</h1>
<p className="text-lg text-white">
Your interview has been successfully completed.
</p>
</div>
);
}
16 changes: 12 additions & 4 deletions app/api/analytics/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,20 @@ import { env } from '~/env.mjs';
import { createRouteHandler } from '@codaco/analytics';
import { WebServiceClient } from '@maxmind/geoip2-node';

const maxMindClient = new WebServiceClient(
env.MAXMIND_ACCOUNT_ID,
env.MAXMIND_LICENSE_KEY,
{
host: 'geolite.info',
},
);

const installationId = await getInstallationId();

const routeHandler = createRouteHandler({
maxMindAccountId: env.MAXMIND_ACCOUNT_ID,
maxMindLicenseKey: env.MAXMIND_LICENSE_KEY,
getInstallationId,
installationId,
platformUrl: 'https://frescoanalytics.networkcanvas.dev',
WebServiceClient,
maxMindClient,
});

export { routeHandler as POST };
14 changes: 8 additions & 6 deletions env.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ export const env = createEnv({
*/
server: {
DATABASE_URL: z.string().url(),
MAXMIND_ACCOUNT_ID: z.string(),
MAXMIND_LICENSE_KEY: z.string(),

INSTALLATION_ID: z.string().optional(),
},

/**
Expand All @@ -23,9 +27,7 @@ export const env = createEnv({
NODE_ENV: z
.enum(['development', 'test', 'production'])
.default('development'),
MAXMIND_ACCOUNT_ID: z.string(),
MAXMIND_LICENSE_KEY: z.string(),
NEXT_PUBLIC_DISABLE_ANALYTICS: z.string().optional(),
DISABLE_ANALYTICS: z.boolean().optional(),
},
/**
* You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g.
Expand All @@ -38,12 +40,12 @@ export const env = createEnv({
VERCEL_URL: process.env.VERCEL_URL,
MAXMIND_ACCOUNT_ID: process.env.MAXMIND_ACCOUNT_ID,
MAXMIND_LICENSE_KEY: process.env.MAXMIND_LICENSE_KEY,
NEXT_PUBLIC_DISABLE_ANALYTICS: process.env.NEXT_PUBLIC_DISABLE_ANALYTICS,
DISABLE_ANALYTICS: !!process.env.DISABLE_ANALYTICS,
INSTALLATION_ID: process.env.INSTALLATION_ID,
},
/**
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially
* useful for Docker builds.
*/
// skipValidation: !!process.env.SKIP_ENV_VALIDATION,
skipValidation: true,
skipValidation: !!process.env.SKIP_ENV_VALIDATION,
});
13 changes: 8 additions & 5 deletions lib/development-protocol/nodeLabelWorker.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
* @return {string|Promise} a label for the input node, or
* a promise that resolves to the label
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function nodeLabelWorker({ node, network }) {

// Examples:
//
// 1. Given name, surname initial
Expand Down Expand Up @@ -69,7 +69,6 @@ function nodeLabelWorker({ node, network }) {
// }, 100);
// });


// For our example worker we will return a different label dependant on the node type.
let label = node.nickname || node.name;

Expand All @@ -79,9 +78,13 @@ function nodeLabelWorker({ node, network }) {

switch (node.networkCanvasType) {
case 'person':
if (network.edges.some(
edge => edge.from === node.networkCanvasId || edge.to === node.networkCanvasId
)) {
if (
network.edges.some(
(edge) =>
edge.from === node.networkCanvasId ||
edge.to === node.networkCanvasId,
)
) {
label = `🔗\u{0a}${label}`;
} else if (node.close_friend) {
label = `❤️\u{0a}${label}`;
Expand Down
Loading

0 comments on commit 3123d8f

Please sign in to comment.