@airtable/blocks/ui#useGlobalConfig JavaScript Examples

The following examples show how to use @airtable/blocks/ui#useGlobalConfig. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: index.js    From apps-print-records with MIT License 6 votes vote down vote up
function PrintRecordsApp() {
    const base = useBase();
    const globalConfig = useGlobalConfig();

    // We want to render the list of records in this table.
    const table = base.getTableByName('Collections');

    // The view ID is stored in globalConfig using ViewPickerSynced.
    const viewId = globalConfig.get(GlobalConfigKeys.VIEW_ID);

    // The view may have been deleted, so we use getViewByIdIfExists
    // instead of getViewById. getViewByIdIfExists will return null
    // if the view doesn't exist.
    const view = table.getViewByIdIfExists(viewId);

    return (
        <div>
            <Toolbar table={table} />
            <Box margin={3}>
                <Report view={view} />
            </Box>
        </div>
    );
}
Example #2
Source File: settings.js    From apps-url-preview with MIT License 6 votes vote down vote up
/**
 * A React hook to validate and access settings configured in SettingsForm.
 * @returns {{settings: *, isValid: boolean, message: string}|{settings: *, isValid: boolean}}
 */
export function useSettings() {
    const base = useBase();
    const globalConfig = useGlobalConfig();
    const settings = getSettings(globalConfig, base);
    return getSettingsValidationResult(settings);
}
Example #3
Source File: settings.js    From neighbor-express with MIT License 6 votes vote down vote up
// A UI component for setting a key in the global config.
// key can be a string (for a top level key) or a list of strings (for a nested value)
function InputSetter({ label, description, keyOrPath }) {
  const globalConfig = useGlobalConfig();

  return (
    <FormField label={label} description={description}>
      <InputSynced
        globalConfigKey={keyOrPath}
        disabled={!globalConfig.hasPermissionToSet(keyOrPath)}
      />
    </FormField>
  );
}
Example #4
Source File: settings.js    From neighbor-express with MIT License 6 votes vote down vote up
function FieldSetter({
  label,
  description,
  keyOrPath,
  tableName,
  ...setterProps
}) {
  const globalConfig = useGlobalConfig();
  const base = useBase();
  const table = base.getTableByNameIfExists(tableName);

  function setField(newField) {
    globalConfig.setAsync(keyOrPath, newField.id);
  }
  // If table is null or undefined, the FieldPicker will not render.
  return (
    <FormField label={label} description={description}>
      <FieldPicker
        table={table}
        field={table.getFieldIfExists(globalConfig.get(keyOrPath))}
        onChange={setField}
        disabled={!globalConfig.hasPermissionToSet(keyOrPath)}
        {...setterProps}
      />
    </FormField>
  );
}
Example #5
Source File: settings.js    From neighbor-express with MIT License 6 votes vote down vote up
function AddEmailTypeDialog() {
  const [isDialogOpen, setIsDialogOpen] = useState(false);
  const [name, setName] = useState("");
  const globalConfig = useGlobalConfig();

  function save() {
    globalConfig.setAsync(["email_types", name], {});
    setIsDialogOpen(false);
  }
  return (
    <>
      <Button onClick={() => setIsDialogOpen(true)}>Add new email type</Button>
      {isDialogOpen && (
        <Dialog onClose={() => setIsDialogOpen(false)} width="320px">
          <Dialog.CloseButton />
          <Heading>New Email Type</Heading>
          <FormField
            label="Name"
            description="A short descriptive name of the new type of email"
          >
            <Input value={name} onChange={(e) => setName(e.target.value)} />
          </FormField>
          <Button onClick={save}>Save</Button>
        </Dialog>
      )}
    </>
  );
}
Example #6
Source File: settings.js    From neighbor-express with MIT License 6 votes vote down vote up
export function SettingsComponent({ exit }) {
  const globalConfig = useGlobalConfig();

  return (
    <Box padding={3}>
      <Button variant="primary" onClick={exit} style={{ float: "right" }}>
        Exit settings
      </Button>
      <h1> Settings </h1>
      <p>
        You probably won't need to do anything here unless you're just starting
        out.
      </p>
      <FormField label="Galaxy Digital API Key">
        <InputSynced globalConfigKey="GALAXY_DIGITAL_API_KEY" />
      </FormField>
    </Box>
  );
}
Example #7
Source File: settings.js    From apps-flashcard with MIT License 5 votes vote down vote up
/**
 * A React hook to validate and access settings configured in SettingsForm.
 * @returns {{
 *  settings: {
 *      table: Table | null,
 *      view: View | null,
 *      questionField: Field | null,
 *      answerField: Field | null,
 *  },
 *  isValid: boolean,
 *  message?: string}}
 */
export function useSettings() {
    const base = useBase();
    const globalConfig = useGlobalConfig();

    const table = base.getTableByIdIfExists(globalConfig.get(ConfigKeys.TABLE_ID));
    const view = table ? table.getViewByIdIfExists(globalConfig.get(ConfigKeys.VIEW_ID)) : null;
    const questionField = table
        ? table.getFieldByIdIfExists(globalConfig.get(ConfigKeys.QUESTION_FIELD_ID))
        : null;
    const answerField = table
        ? table.getFieldByIdIfExists(globalConfig.get(ConfigKeys.ANSWER_FIELD_ID))
        : null;
    const settings = {
        table,
        view,
        questionField,
        answerField,
    };

    if (!table || !view || !questionField) {
        return {
            isValid: false,
            message: 'Pick a table, view, and question field',
            settings,
        };
    }
    return {
        isValid: true,
        settings,
    };
}
Example #8
Source File: index.js    From apps-simple-chart with MIT License 5 votes vote down vote up
function SimpleChartApp() {
    const base = useBase();
    const globalConfig = useGlobalConfig();

    const tableId = globalConfig.get(GlobalConfigKeys.TABLE_ID);
    const table = base.getTableByIdIfExists(tableId);

    const viewId = globalConfig.get(GlobalConfigKeys.VIEW_ID);
    const view = table ? table.getViewByIdIfExists(viewId) : null;

    const xFieldId = globalConfig.get(GlobalConfigKeys.X_FIELD_ID);
    const xField = table ? table.getFieldByIdIfExists(xFieldId) : null;

    const records = useRecords(view);

    const data = records && xField ? getChartData({records, xField}) : null;

    return (
        <Box
            position="absolute"
            top={0}
            left={0}
            right={0}
            bottom={0}
            display="flex"
            flexDirection="column"
        >
            <Settings table={table} />
            {data && (
                <Box position="relative" flex="auto" padding={3}>
                    <Bar
                        data={data}
                        options={{
                            maintainAspectRatio: false,
                            scales: {
                                yAxes: [
                                    {
                                        ticks: {
                                            beginAtZero: true,
                                        },
                                    },
                                ],
                            },
                            legend: {
                                display: false,
                            },
                        }}
                    />
                </Box>
            )}
        </Box>
    );
}
Example #9
Source File: todo-app.js    From apps-todo-list with MIT License 5 votes vote down vote up
export default function TodoApp() {
    const base = useBase();

    // Read the user's choice for which table and view to use from globalConfig.
    const globalConfig = useGlobalConfig();
    const tableId = globalConfig.get('selectedTableId');
    const viewId = globalConfig.get('selectedViewId');
    const doneFieldId = globalConfig.get('selectedDoneFieldId');

    const table = base.getTableByIdIfExists(tableId);
    const view = table ? table.getViewByIdIfExists(viewId) : null;
    const doneField = table ? table.getFieldByIdIfExists(doneFieldId) : null;

    // Don't need to fetch records if doneField doesn't exist (the field or it's parent table may
    // have been deleted, or may not have been selected yet.)
    const records = useRecords(doneField ? view : null, {
        fields: doneField ? [table.primaryField, doneField] : [],
    });

    const tasks = records
        ? records.map(record => {
              return <Task key={record.id} record={record} table={table} doneField={doneField} />;
          })
        : null;

    return (
        <div>
            <Box padding={3} borderBottom="thick">
                <FormField label="Table">
                    <TablePickerSynced globalConfigKey="selectedTableId" />
                </FormField>
                <FormField label="View">
                    <ViewPickerSynced table={table} globalConfigKey="selectedViewId" />
                </FormField>
                <FormField label="Field" marginBottom={0}>
                    <FieldPickerSynced
                        table={table}
                        globalConfigKey="selectedDoneFieldId"
                        placeholder="Pick a 'done' field..."
                        allowedTypes={[FieldType.CHECKBOX]}
                    />
                </FormField>
            </Box>
            {tasks}
            {table && doneField && <AddTaskForm table={table} />}
        </div>
    );
}
Example #10
Source File: SettingsForm.js    From apps-url-preview with MIT License 5 votes vote down vote up
function SettingsForm({setIsSettingsOpen}) {
    const globalConfig = useGlobalConfig();
    const {
        isValid,
        message,
        settings: {isEnforced, urlTable},
    } = useSettings();

    const canUpdateSettings = globalConfig.hasPermissionToSet();

    return (
        <Box
            position="absolute"
            top={0}
            bottom={0}
            left={0}
            right={0}
            display="flex"
            flexDirection="column"
        >
            <Box flex="auto" padding={4} paddingBottom={2}>
                <Heading marginBottom={3}>Settings</Heading>
                <FormField label="">
                    <Switch
                        aria-label="When enabled, the app will only show previews for the specified table and field, regardless of what field is selected."
                        value={isEnforced}
                        onChange={value => {
                            globalConfig.setAsync(ConfigKeys.IS_ENFORCED, value);
                        }}
                        disabled={!canUpdateSettings}
                        label="Use a specific field for previews"
                    />
                    <Text paddingY={1} textColor="light">
                        {isEnforced
                            ? 'The app will show previews for the selected record in grid view if the table has a supported URL in the specified field.'
                            : 'The app will show previews if the selected cell in grid view has a supported URL.'}
                    </Text>
                </FormField>
                {isEnforced && (
                    <FormField label="Preview table">
                        <TablePickerSynced globalConfigKey={ConfigKeys.URL_TABLE_ID} />
                    </FormField>
                )}
                {isEnforced && urlTable && (
                    <FormField label="Preview field">
                        <FieldPickerSynced
                            table={urlTable}
                            globalConfigKey={ConfigKeys.URL_FIELD_ID}
                            allowedTypes={allowedUrlFieldTypes}
                        />
                    </FormField>
                )}
            </Box>
            <Box display="flex" flex="none" padding={3} borderTop="thick">
                <Box
                    flex="auto"
                    display="flex"
                    alignItems="center"
                    justifyContent="flex-end"
                    paddingRight={2}
                >
                    <Text textColor="light">{message}</Text>
                </Box>
                <Button
                    disabled={!isValid}
                    size="large"
                    variant="primary"
                    onClick={() => setIsSettingsOpen(false)}
                >
                    Done
                </Button>
            </Box>
        </Box>
    );
}
Example #11
Source File: settings.js    From neighbor-express with MIT License 5 votes vote down vote up
function EmailTypeSettings({ emailType }) {
  const globalConfig = useGlobalConfig();

  const audienceOptions = [
    { value: "volunteer", label: "Volunteer" },
    { value: "recipient", label: "Delivery Recipient" },
  ];

  const deliveriesTable = useBase().getTableByNameIfExists("Deliveries");
  const triggerField = deliveriesTable.getFieldIfExists(
    globalConfig.get("trigger_column")
  );

  const stageOptions = triggerField?.options.choices.map((choice) => {
    return {
      value: choice.id,
      label: choice.name,
    };
  });

  function deleteMe() {
    globalConfig.setAsync(["email_types", emailType], undefined);
  }

  return (
    <Box padding={3}>
      <TextButton onClick={deleteMe} icon="trash" style={{ float: "right" }}>
        Delete
      </TextButton>
      <Accordion title={emailType}>
        <InputSetter
          label="Sendgrid Template ID"
          description="Find this in the Sendgrid UI. Looks like d-ea13bf1f408947829fa19779eade8250"
          keyOrPath={["email_types", emailType, "sendgrid_template"]}
        />
        <FormField
          label="Send to..."
          description="Who should this email be sent to?"
        >
          <SelectButtonsSynced
            globalConfigKey={["email_types", emailType, "audience"]}
            options={audienceOptions}
            width="320px"
          />
        </FormField>
        <FormField
          label="Send when..."
          description={`Email will be sent when ${triggerField.name} column is...`}
        >
          <SelectSynced
            globalConfigKey={["email_types", emailType, "stage"]}
            options={stageOptions}
            width="320px"
          />
        </FormField>
      </Accordion>
    </Box>
  );
}
Example #12
Source File: settings.js    From neighbor-express with MIT License 5 votes vote down vote up
function AddTemplateVariableDialog({ table }) {
  const [isDialogOpen, setIsDialogOpen] = useState(false);
  const [field, setField] = useState("");
  const [sendgrid, setSendgrid] = useState("");
  const globalConfig = useGlobalConfig();

  function save() {
    globalConfig.setAsync(
      ["template_variables", table.name, field.id],
      sendgrid
    );
    setField("");
    setSendgrid("");
    setIsDialogOpen(false);
  }

  return (
    <>
      <Button onClick={() => setIsDialogOpen(true)}>
        Add new template variable
      </Button>
      {isDialogOpen && (
        <Dialog onClose={() => setIsDialogOpen(false)} width="320px">
          <Dialog.CloseButton />
          <FormField
            label="Airtable field"
            description="What field contains the data you want to send to sendgrid?"
          >
            <FieldPicker
              table={table}
              field={field}
              onChange={(newField) => setField(newField)}
            />
          </FormField>
          <FormField
            label="Sendgrid reference"
            description="How does the sengrid template refer to this data?"
          >
            <Input
              value={sendgrid}
              onChange={(e) => setSendgrid(e.target.value)}
            />
          </FormField>
          <Button onClick={save}>Save</Button>
        </Dialog>
      )}
    </>
  );
}
Example #13
Source File: settings.js    From neighbor-express with MIT License 5 votes vote down vote up
function TableTemplateVariables({ tableName }) {
  const globalConfig = useGlobalConfig();
  if (globalConfig.get(["template_variables", tableName]) === undefined) {
    globalConfig.setAsync(["template_variables", tableName], {});
  }
  const base = useBase();
  const table = base.getTableByNameIfExists(tableName);

  const tableNameToSendgrid = {
    Deliveries: "delivery",
    Volunteers: "volunteer",
  };

  return (
    <Box padding={3}>
      <Text>
        {" "}
        You can use these fields from the {table.name} table in sendgrid{" "}
      </Text>
      <ul>
        {Object.keys(globalConfig.get(["template_variables", table.name])).map(
          (f_id) => {
            const field = table.getFieldIfExists(f_id);
            // this should be deletable
            const sendgridValue = globalConfig.get([
              "template_variables",
              table.name,
              f_id,
            ]);
            const sendgridFormat = `{{${tableNameToSendgrid[tableName]}.${sendgridValue}}}`;
            const removeLink = (
              <TextButton
                onClick={() => {
                  globalConfig.setAsync(
                    ["template_variables", table.name, f_id],
                    undefined
                  );
                }}
              >
                (remove)
              </TextButton>
            );
            return (
              <li key={f_id}>
                {" "}
                {field.name} -> {sendgridFormat} {removeLink}
              </li>
            );
          }
        )}
      </ul>
      <AddTemplateVariableDialog table={table} />
    </Box>
  );
}
Example #14
Source File: settings.js    From apps-base-schema with MIT License 4 votes vote down vote up
/**
 * Reads values from GlobalConfig and calculates relevant positioning information for the nodes
 * and links.
 *
 * A node represents either a "row" in the visualization - either a table header or a field. A link
 * represents a relationship between two nodes. We persist two types of information in globalConfig:
 * (1) whether a certain link type should be shown; and (2) the x,y position for each table, where
 * position indicates the top-left corner of the table.
 *
 * Positioning calculation takes place as follows:
 * (1) Parse the schema of the base (ie, what tables exist, what fields exist on those tables,
 * and what are the relationships/links between fields & tables).
 * (2) Lookup the persisted position for each table, and check for any recently-created tables that
 * are not accounted for in these persisted settings. Assign positions for any new tables.
 * (3) Using the table & link configurations from step 1 and table coordinates from step 2,
 * calculate the paths (ie, the `d` attribute for SVG element) for the links. Because the row widths
 * & heights are constant, we can infer coordinates by adding offsets to the table coordinates.
 *
 * When dragging a table and updating positions on `mousemove`, it is inefficient to go through this
 * calculation process / rely on React state updates to propagate down to the child components.
 * Instead, we only calculate required changes and directly manipulate the DOM (@see DragWrapper).
 * The new table coordinates are persisted to globalConfig when dragging is finished, but positions
 * and paths for nodes and links are only recalculated from scratch when the base schema changes.
 * Otherwise, we just rely on the current DOM position.
 *
 * @returns {{
 *     enabledLinksByType: { ['multipleRecordLinks' | 'formula' | 'multipleLookupValues' | 'rollup' | 'count']: boolean },
 *     tableCoordsByTableId: { TableId: { x: number, y: number }},
 *     tableConfigsByTableId: { TableId: { tableNode: Node, fieldNodes: Node[] }},
 *     nodesById: { NodeId: Node },
 *     linksById: { LinkId: Link },
 *     linkPathsByLinkId: { LinkId: string },
 *     dependentLinksByNodeId: { NodeId: Link[] }
 * }}
 */
export default function useSettings() {
    const [baseSchema, setBaseSchema] = useState(() => parseSchema(base));
    const {nodesById, linksById, tableConfigsByTableId, dependentLinksByNodeId} = baseSchema;
    const globalConfig = useGlobalConfig();
    let tableCoordsByTableId;

    if (!globalConfig.get(ConfigKeys.TABLE_COORDS_BY_TABLE_ID)) {
        // First time run, determine initial table coords
        tableCoordsByTableId = getInitialTableCoords(tableConfigsByTableId);
        globalConfig.setPathsAsync([
            {
                path: [ConfigKeys.TABLE_COORDS_BY_TABLE_ID],
                value: tableCoordsByTableId,
            },
            {
                path: [ConfigKeys.ENABLED_LINKS_BY_TYPE],
                value: {
                    [FieldType.MULTIPLE_RECORD_LINKS]: true,
                    [FieldType.FORMULA]: true,
                    [FieldType.ROLLUP]: true,
                    [FieldType.COUNT]: true,
                    [FieldType.MULTIPLE_LOOKUP_VALUES]: true,
                },
            },
        ]);
    } else {
        // Non-first time run, check for any new tables missing from the old saved coords
        tableCoordsByTableId = globalConfig.get(ConfigKeys.TABLE_COORDS_BY_TABLE_ID);
        if (
            _.difference(Object.keys(tableConfigsByTableId), Object.keys(tableCoordsByTableId))
                .length > 0
        ) {
            tableCoordsByTableId = getUpdatedTableCoords(
                tableConfigsByTableId,
                tableCoordsByTableId,
            );
            if (globalConfig.hasPermissionToSet()) {
                globalConfig.setAsync(ConfigKeys.TABLE_COORDS_BY_TABLE_ID, tableCoordsByTableId);
            }
        }
    }
    const [linkPathsByLinkId, setLinkPathsByLinkId] = useState(() =>
        calculateLinkPaths(linksById, tableConfigsByTableId, tableCoordsByTableId),
    );

    // Only re-perform these potentially expensive calclulations when required, when the base
    // schema changes (ie, table added/removed/renamed, field added/removed/renamed).
    useWatchable(base, ['schema'], () => {
        const newSchema = parseSchema(base);
        const newTableCoords = getUpdatedTableCoords(
            newSchema.tableConfigsByTableId,
            tableCoordsByTableId,
        );
        const newLinkPaths = calculateLinkPaths(
            newSchema.linksById,
            newSchema.tableConfigsByTableId,
            newTableCoords,
        );

        if (globalConfig.hasPermissionToSet()) {
            globalConfig.setAsync(ConfigKeys.TABLE_COORDS_BY_TABLE_ID, newTableCoords);
        }
        setBaseSchema(newSchema);
        setLinkPathsByLinkId(newLinkPaths);
    });

    const enabledLinksByType = {
        [FieldType.MULTIPLE_RECORD_LINKS]: globalConfig.get([
            ConfigKeys.ENABLED_LINKS_BY_TYPE,
            FieldType.MULTIPLE_RECORD_LINKS,
        ]),
        [FieldType.FORMULA]: globalConfig.get([
            ConfigKeys.ENABLED_LINKS_BY_TYPE,
            FieldType.FORMULA,
        ]),
        [FieldType.ROLLUP]: globalConfig.get([ConfigKeys.ENABLED_LINKS_BY_TYPE, FieldType.ROLLUP]),
        [FieldType.MULTIPLE_LOOKUP_VALUES]: globalConfig.get([
            ConfigKeys.ENABLED_LINKS_BY_TYPE,
            FieldType.MULTIPLE_LOOKUP_VALUES,
        ]),
        [FieldType.COUNT]: globalConfig.get([ConfigKeys.ENABLED_LINKS_BY_TYPE, FieldType.COUNT]),
    };

    return {
        enabledLinksByType,
        tableCoordsByTableId,
        tableConfigsByTableId,
        nodesById,
        linksById,
        linkPathsByLinkId,
        dependentLinksByNodeId,
    };
}
Example #15
Source File: index.js    From blocks-usa-map with MIT License 4 votes vote down vote up
function USAMapBlock() {
    const [isShowingSettings, setIsShowingSettings] = useState(false);
    const [selectedState, setSelectedState] = useState(null);

    useSettingsButton(function() {
        setIsShowingSettings(!isShowingSettings);
    });

    const viewport = useViewport();

    const base = useBase();
    const globalConfig = useGlobalConfig();
    const tableId = globalConfig.get('selectedTableId');
    const stateFieldId = globalConfig.get('selectedStateFieldId');
    const colorFieldId = globalConfig.get('selectedColorFieldId');

    const table = base.getTableByIdIfExists(tableId);
    const stateField = table ? table.getFieldByIdIfExists(stateFieldId) : null;
    const colorField = table ? table.getFieldByIdIfExists(colorFieldId) : null;

    // if (table == null || stateField == null || colorField == null) {
    //     setIsShowingSettings(true);
    // }

    const records = useRecords(stateField ? table : null);

    let mapData = null;
    if (stateField !== null && colorField !== null) {
        mapData = getMapData(records, stateField, colorField);
    }

    const mapHandler = (event) => {
        setSelectedState(event.target.dataset.name);
    };

    // If settings is showing, draw settings only
    if (isShowingSettings) {
        return (
            <Box padding={3} display="flex">
                <FormField
                    label="Table"
                    description="Choose the table you want to your State data to come from."
                    padding={1}
                    marginBottom={0}
                >
                    <TablePickerSynced globalConfigKey="selectedTableId" />
                </FormField>
                <FormField
                    label="State Field"
                    description='The State field will select a state by either abbreviation ("NJ") or name ("New Jersey")'
                    marginBottom={0}
                    padding={1}
                >
                    <FieldPickerSynced
                        table={table}
                        globalConfigKey="selectedStateFieldId"
                        placeholder="Pick a 'state' field..."
                        allowedTypes={[FieldType.SINGLE_LINE_TEXT, FieldType.SINGLE_SELECT, FieldType.MULTIPLE_RECORD_LINKS, FieldType.MULTIPLE_LOOKUP_VALUES]}
                    />
                </FormField>
                <FormField
                    label="Color Field"
                    marginBottom={0}
                    description="Choose the state color using either a text field which describes the color name, or a single select."
                    padding={1}
                >
                    <FieldPickerSynced
                        table={table}
                        globalConfigKey="selectedColorFieldId"
                        placeholder="Pick a 'color' field..."
                        allowedTypes={[FieldType.SINGLE_LINE_TEXT, FieldType.SINGLE_SELECT, FieldType.MULTIPLE_LOOKUP_VALUES, FieldType.NUMBER]}
                    />

                {colorField != null && colorField.type === FieldType.NUMBER &&
                    <text>You have selected a numeric type; state colors will be normalized to the maximum value in your field.</text>
                }
                </FormField>
            </Box>
        )
    }
    // otherwise draw the map.
    return (
        <div>
            <Box border="default"
                 backgroundColor="lightGray1"
                //  padding={}
                 >
                {/* TODO Allow selected state to show a column of data. */}
                {/* {selectedState
                    ? <SelectedState selected={selectedState}/>
                    : <div>Click to select a state</div>
                } */}
                <USAMap
                    title="USA USA USA"
                    width={viewport.size.width}
                    height={viewport.size.height - 5}
                    customize={mapData}
                    onClick={mapHandler}
                />
            </Box>
        </div>
    )
}
Example #16
Source File: settings.js    From neighbor-express with MIT License 4 votes vote down vote up
export function SettingsComponent({ exit }) {
  const globalConfig = useGlobalConfig();

  if (globalConfig.get("template_variables") === undefined) {
    globalConfig.setAsync("template_variables", {});
  }

  if (globalConfig.get("email_types") === undefined) {
    globalConfig.setAsync("email_types", {});
  }

  return (
    <Box padding={3}>
      <Button variant="primary" onClick={exit} style={{ float: "right" }}>
        Exit settings
      </Button>
      <h1> Settings </h1>
      <p>
        {" "}
        You probably won't need to do anything here unless you're just starting
        out.{" "}
      </p>
      <Accordion title="Global">
        <InputSetter
          label="Organization name"
          description="When people reply to your emails, what is the name they will see?"
          keyOrPath={["reply_to", "name"]}
        />
        <InputSetter
          label="Reply email"
          description="What email address should people use to reply to your emails?"
          keyOrPath={["reply_to", "email"]}
        />
        <InputSetter
          label="Sendgrid proxy token"
          description="This is a secret token that is used to authenticate sending the email"
          keyOrPath="SENDGRID_PROXY_TOKEN"
        />
        <FieldSetter
          label="Trigger column"
          description="Which column should be used to determine whether an email is sent?"
          keyOrPath="trigger_column"
          tableName="Deliveries"
          allowedTypes={[FieldType.SINGLE_SELECT]}
        />
      </Accordion>
      <Accordion title="Email Types">
        <h4>Delivery Emails</h4>
        <Text>
          Here you can configure emails to go out at various stages of a
          delivery. Emails can be set up for both the delivery recipient and the
          volunteer.
        </Text>
        {Object.keys(globalConfig.get("email_types")).map((emailType) => {
          return <EmailTypeSettings key={emailType} emailType={emailType} />;
        })}
        <AddEmailTypeDialog />
        <Box>
          <h4>Volunteer Emails</h4>
          <Text>
            Enable the setting below if you want to send volunteers a welcome
            email when they first sign up.
          </Text>
          <Switch
            value={!!globalConfig.get("enable_volunteer_welcome_email")}
            onChange={(newValue) =>
              globalConfig.setAsync("enable_volunteer_welcome_email", newValue)
            }
            label="Email volunteers on inital signup"
            width="320px"
          />
          {globalConfig.get("enable_volunteer_welcome_email") ? (
            <>
              <Label htmlFor="volunteer-welcome-template-id-input">
                Volunteer welcome email sendgrid template ID
              </Label>
              <Input
                id="volunteer-welcome-template-id-input"
                value={
                  globalConfig.get("volunteer_welcome_email_template_id") || ""
                }
                onChange={(e) =>
                  globalConfig.setAsync(
                    "volunteer_welcome_email_template_id",
                    e.target.value
                  )
                }
                placeholder="Enter Sendgrid Template ID here"
              />
              <FieldSetter
                label="Volunteer name field"
                description="The field containing volunteers names"
                keyOrPath="volunteer_name_field"
                tableName="Volunteers"
              />
            </>
          ) : null}
        </Box>
      </Accordion>
      <Accordion title="Template Variables">
        {["Deliveries", "Volunteers"].map((tableName) => {
          return (
            <TableTemplateVariables key={tableName} tableName={tableName} />
          );
        })}
      </Accordion>
    </Box>
  );
}