@remit/web-client 0.0.300 → 0.0.301

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,97 @@
1
+ import type {
2
+ AccountOperationsCreateAccountResponse,
3
+ AccountOperationsTestConnectionResponse,
4
+ MicrosoftOAuthOperationsMicrosoftOAuthStartResponse,
5
+ RemitImapAccountSyncStatusResponse,
6
+ SyncOperationsGetSyncStatusResponse,
7
+ } from "@remit/api-http-client/types.gen.ts";
8
+ import { type HttpHandler, HttpResponse, http } from "msw";
9
+ import { makeAccount } from "@/test-support/fixtures";
10
+
11
+ const API = "/api";
12
+
13
+ export const NEW_ACCOUNT_ID = "acc-new";
14
+
15
+ export type Autoconfig = "found" | "missing" | "slow";
16
+
17
+ export interface OnboardingHandlerOptions {
18
+ autoconfig?: Autoconfig;
19
+ connection?: AccountOperationsTestConnectionResponse;
20
+ createFails?: boolean;
21
+ sync?: Omit<RemitImapAccountSyncStatusResponse, "accountId">;
22
+ }
23
+
24
+ const autoconfigXml = (domain: string): string => `<?xml version="1.0"?>
25
+ <clientConfig version="1.1">
26
+ <emailProvider id="${domain}">
27
+ <incomingServer type="imap">
28
+ <hostname>mail.${domain}</hostname>
29
+ <port>993</port>
30
+ <socketType>SSL</socketType>
31
+ </incomingServer>
32
+ <outgoingServer type="smtp">
33
+ <hostname>mail.${domain}</hostname>
34
+ <port>465</port>
35
+ <socketType>SSL</socketType>
36
+ </outgoingServer>
37
+ </emailProvider>
38
+ </clientConfig>`;
39
+
40
+ const pending = (): Promise<never> => new Promise<never>(() => undefined);
41
+
42
+ const autoconfigHandlers = (autoconfig: Autoconfig): HttpHandler[] => [
43
+ http.get("https://autoconfig.thunderbird.net/v1.1/:domain", ({ params }) => {
44
+ if (autoconfig === "slow") return pending();
45
+ if (autoconfig === "missing")
46
+ return new HttpResponse(null, { status: 404 });
47
+ return HttpResponse.text(autoconfigXml(String(params.domain)), {
48
+ headers: { "Content-Type": "application/xml" },
49
+ });
50
+ }),
51
+ http.get(
52
+ /^https:\/\/autoconfig\.[^/]+\/mail\/config-v1\.1\.xml$/,
53
+ () => new HttpResponse(null, { status: 404 }),
54
+ ),
55
+ ];
56
+
57
+ export const onboardingHandlers = ({
58
+ autoconfig = "found",
59
+ connection = { imapSuccess: true, smtpSuccess: true },
60
+ createFails = false,
61
+ sync = {
62
+ syncPhase: "complete",
63
+ mailboxCountTotal: 6,
64
+ mailboxCountSynced: 6,
65
+ mailboxes: [],
66
+ },
67
+ }: OnboardingHandlerOptions = {}): HttpHandler[] => [
68
+ ...autoconfigHandlers(autoconfig),
69
+ http.post(`${API}/accounts/test-connection`, () =>
70
+ HttpResponse.json<AccountOperationsTestConnectionResponse>(connection),
71
+ ),
72
+ http.post(`${API}/accounts`, async ({ request }) => {
73
+ if (createFails) return HttpResponse.error();
74
+ const body: unknown = await request.json();
75
+ const email =
76
+ typeof body === "object" &&
77
+ body !== null &&
78
+ "email" in body &&
79
+ typeof body.email === "string"
80
+ ? body.email
81
+ : "alice@northwind.example";
82
+ return HttpResponse.json<AccountOperationsCreateAccountResponse>(
83
+ makeAccount({ accountId: NEW_ACCOUNT_ID, email }),
84
+ );
85
+ }),
86
+ http.get(`${API}/accounts/${NEW_ACCOUNT_ID}/sync/status`, () =>
87
+ HttpResponse.json<SyncOperationsGetSyncStatusResponse>({
88
+ accountId: NEW_ACCOUNT_ID,
89
+ ...sync,
90
+ }),
91
+ ),
92
+ http.post(`${API}/accounts/oauth/microsoft/start`, () =>
93
+ HttpResponse.json<MicrosoftOAuthOperationsMicrosoftOAuthStartResponse>({
94
+ authorizationUrl: "#microsoft-sign-in",
95
+ }),
96
+ ),
97
+ ];
@@ -0,0 +1,19 @@
1
+ import { type AuthProvider, noneAuthProvider } from "@/auth/provider";
2
+
3
+ type RuntimeConfig = NonNullable<typeof globalThis.__REMIT_CONFIG__>;
4
+
5
+ export const signedInAs = (email: string): AuthProvider => ({
6
+ ...noneAuthProvider,
7
+ Account: ({ children }) => (
8
+ <>{children({ email, signOut: () => undefined })}</>
9
+ ),
10
+ });
11
+
12
+ export const withRuntimeConfig =
13
+ (config: RuntimeConfig) => (): (() => void) => {
14
+ const previous = globalThis.__REMIT_CONFIG__;
15
+ globalThis.__REMIT_CONFIG__ = config;
16
+ return () => {
17
+ globalThis.__REMIT_CONFIG__ = previous;
18
+ };
19
+ };
@@ -107,6 +107,7 @@ const REAUTH_ACCOUNT = {
107
107
  authType: "oauthMicrosoft",
108
108
  connectionState: "reauth_required",
109
109
  syncedServices: ["Mail", "Calendar"],
110
+ grantedScopes: [],
110
111
  };
111
112
 
112
113
  // The router reads `self` at construction; the shared jsdom globals stop at
@@ -22,6 +22,7 @@ import { useEffect, useState } from "react";
22
22
  import { z } from "zod";
23
23
  import { OnboardingWizard } from "@/components/onboarding/OnboardingWizard";
24
24
  import { AccountFormPanel } from "@/components/settings/AccountFormPanel";
25
+ import { AccountServices } from "@/components/settings/AccountServices";
25
26
  import { DangerZone } from "@/components/settings/DangerZone";
26
27
  import { ErrorState } from "@/components/ui/ErrorState";
27
28
  import {
@@ -482,26 +483,33 @@ function AccountsSettings() {
482
483
  );
483
484
 
484
485
  return (
485
- <AccountHealthCard
486
- key={account.accountId}
487
- label={accountLabel(account)}
488
- email={account.email}
489
- connector={isOAuthAccount ? "Microsoft 365" : "IMAP"}
490
- syncLabel={deriveSyncLabel(account)}
491
- state={deriveState(account)}
492
- // What the mail server said, when it said anything. Not every
493
- // refusal that fences an account is one the Reconnect button
494
- // clears — "SmtpClientAuthentication is disabled" is a tenant
495
- // setting — so the stored reason outranks the generic prompt.
496
- errorDetail={
497
- isReauth
498
- ? (account.lastError ?? "Re-authentication required")
499
- : isMissingPassword
500
- ? "Imported from a config file, which carries no password. Enter one to start syncing."
501
- : account.lastError
502
- }
503
- trailing={trailingButton}
504
- />
486
+ <div key={account.accountId} className="space-y-2">
487
+ <AccountHealthCard
488
+ label={accountLabel(account)}
489
+ email={account.email}
490
+ connector={isOAuthAccount ? "Microsoft 365" : "IMAP"}
491
+ syncLabel={deriveSyncLabel(account)}
492
+ state={deriveState(account)}
493
+ // What the mail server said, when it said anything. Not every
494
+ // refusal that fences an account is one the Reconnect button
495
+ // clears — "SmtpClientAuthentication is disabled" is a tenant
496
+ // setting — so the stored reason outranks the generic prompt.
497
+ errorDetail={
498
+ isReauth
499
+ ? (account.lastError ?? "Re-authentication required")
500
+ : isMissingPassword
501
+ ? "Imported from a config file, which carries no password. Enter one to start syncing."
502
+ : account.lastError
503
+ }
504
+ trailing={trailingButton}
505
+ />
506
+ <AccountServices
507
+ account={account}
508
+ onRemoveAccount={() =>
509
+ setDeletingAccountId(account.accountId)
510
+ }
511
+ />
512
+ </div>
505
513
  );
506
514
  })}
507
515
  </div>