Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ module.exports = {
{
// default exports are needed in the route modules and the config files,
// but we want to avoid them anywhere else
files: ['app/pages/**/*', 'app/layouts/**/*', '*.config.ts'],
files: ['app/pages/**/*', 'app/layouts/**/*', 'app/forms/**/*', '*.config.ts'],
rules: { 'import/no-default-export': 'off' },
},
{
Expand Down
7 changes: 5 additions & 2 deletions app/forms/firewall-rules-create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {

import { SideModalForm } from '~/components/form/SideModalForm'
import { HL } from '~/components/HL'
import { titleCrumb } from '~/hooks/use-crumbs'
import { getVpcSelector, useVpcSelector } from '~/hooks/use-params'
import { addToast } from '~/stores/toast'
import { ALL_ISH } from '~/util/consts'
Expand All @@ -27,6 +28,8 @@ import { pb } from '~/util/path-builder'
import { CommonFields } from './firewall-rules-common'
import { valuesToRuleUpdate, type FirewallRuleValues } from './firewall-rules-util'

export const handle = titleCrumb('New Rule')

/** Empty form for when we're not creating from an existing rule */
const defaultValuesEmpty: FirewallRuleValues = {
enabled: true,
Expand Down Expand Up @@ -55,7 +58,7 @@ const ruleToValues = (rule: VpcFirewallRule): FirewallRuleValues => ({
hosts: rule.filters.hosts || [],
})

CreateFirewallRuleForm.loader = async ({ params }: LoaderFunctionArgs) => {
export async function clientLoader({ params }: LoaderFunctionArgs) {
const { project, vpc } = getVpcSelector(params)
await Promise.all([
apiQueryClient.prefetchQuery('vpcFirewallRulesView', { query: { project, vpc } }),
Expand All @@ -69,7 +72,7 @@ CreateFirewallRuleForm.loader = async ({ params }: LoaderFunctionArgs) => {
return null
}

export function CreateFirewallRuleForm() {
export default function CreateFirewallRuleForm() {
const vpcSelector = useVpcSelector()
const queryClient = useApiQueryClient()

Expand Down
7 changes: 5 additions & 2 deletions app/forms/firewall-rules-edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
import { trigger404 } from '~/components/ErrorBoundary'
import { SideModalForm } from '~/components/form/SideModalForm'
import { HL } from '~/components/HL'
import { titleCrumb } from '~/hooks/use-crumbs'
import {
getFirewallRuleSelector,
useFirewallRuleSelector,
Expand All @@ -32,7 +33,9 @@ import { pb } from '~/util/path-builder'
import { CommonFields } from './firewall-rules-common'
import { valuesToRuleUpdate, type FirewallRuleValues } from './firewall-rules-util'

EditFirewallRuleForm.loader = async ({ params }: LoaderFunctionArgs) => {
export const handle = titleCrumb('Edit Rule')

export async function clientLoader({ params }: LoaderFunctionArgs) {
const { project, vpc, rule } = getFirewallRuleSelector(params)

const [firewallRules] = await Promise.all([
Expand All @@ -50,7 +53,7 @@ EditFirewallRuleForm.loader = async ({ params }: LoaderFunctionArgs) => {
return null
}

export function EditFirewallRuleForm() {
export default function EditFirewallRuleForm() {
const { project, vpc, rule } = useFirewallRuleSelector()
const vpcSelector = useVpcSelector()
const queryClient = useApiQueryClient()
Expand Down
5 changes: 4 additions & 1 deletion app/forms/floating-ip-create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { ListboxField } from '~/components/form/fields/ListboxField'
import { NameField } from '~/components/form/fields/NameField'
import { SideModalForm } from '~/components/form/SideModalForm'
import { HL } from '~/components/HL'
import { titleCrumb } from '~/hooks/use-crumbs'
import { useProjectSelector } from '~/hooks/use-params'
import { addToast } from '~/stores/toast'
import { Message } from '~/ui/lib/Message'
Expand All @@ -36,7 +37,9 @@ const defaultValues: Omit<FloatingIpCreate, 'ip'> = {
pool: undefined,
}

export function CreateFloatingIpSideModalForm() {
export const handle = titleCrumb('New Floating IP')

export default function CreateFloatingIpSideModalForm() {
// Fetch 1000 to we can be sure to get them all. Don't bother prefetching
// because the list is hidden under the Advanced accordion.
const { data: allPools } = useApiQuery('projectIpPoolList', { query: { limit: ALL_ISH } })
Expand Down
7 changes: 5 additions & 2 deletions app/forms/floating-ip-edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { DescriptionField } from '~/components/form/fields/DescriptionField'
import { NameField } from '~/components/form/fields/NameField'
import { SideModalForm } from '~/components/form/SideModalForm'
import { HL } from '~/components/HL'
import { titleCrumb } from '~/hooks/use-crumbs'
import { getFloatingIpSelector, useFloatingIpSelector } from '~/hooks/use-params'
import { addToast } from '~/stores/toast'
import { EmptyCell } from '~/table/cells/EmptyCell'
Expand All @@ -35,7 +36,7 @@ const floatingIpView = ({ project, floatingIp }: PP.FloatingIp) =>
const instanceList = (project: string) =>
getListQFn('instanceList', { query: { project, limit: ALL_ISH } })

EditFloatingIpSideModalForm.loader = async ({ params }: LoaderFunctionArgs) => {
export async function clientLoader({ params }: LoaderFunctionArgs) {
const selector = getFloatingIpSelector(params)
await Promise.all([
queryClient.fetchQuery(floatingIpView(selector)),
Expand All @@ -44,7 +45,9 @@ EditFloatingIpSideModalForm.loader = async ({ params }: LoaderFunctionArgs) => {
return null
}

export function EditFloatingIpSideModalForm() {
export const handle = titleCrumb('Edit Floating IP')

export default function EditFloatingIpSideModalForm() {
const navigate = useNavigate()

const floatingIpSelector = useFloatingIpSelector()
Expand Down
5 changes: 4 additions & 1 deletion app/forms/idp/create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { NameField } from '~/components/form/fields/NameField'
import { TextField } from '~/components/form/fields/TextField'
import { SideModalForm } from '~/components/form/SideModalForm'
import { HL } from '~/components/HL'
import { titleCrumb } from '~/hooks/use-crumbs'
import { useSiloSelector } from '~/hooks/use-params'
import { addToast } from '~/stores/toast'
import { Checkbox } from '~/ui/lib/Checkbox'
Expand Down Expand Up @@ -50,7 +51,9 @@ const defaultValues: IdpCreateFormValues = {
},
}

export function CreateIdpSideModalForm() {
export const handle = titleCrumb('New Identity Provider')

export default function CreateIdpSideModalForm() {
const navigate = useNavigate()
const queryClient = useApiQueryClient()

Expand Down
7 changes: 5 additions & 2 deletions app/forms/idp/edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ import { DescriptionField } from '~/components/form/fields/DescriptionField'
import { NameField } from '~/components/form/fields/NameField'
import { TextField } from '~/components/form/fields/TextField'
import { SideModalForm } from '~/components/form/SideModalForm'
import { titleCrumb } from '~/hooks/use-crumbs'
import { getIdpSelector, useIdpSelector } from '~/hooks/use-params'
import { FormDivider } from '~/ui/lib/Divider'
import { PropertiesTable } from '~/ui/lib/PropertiesTable'
import { ResourceLabel, SideModal } from '~/ui/lib/SideModal'
import { pb } from '~/util/path-builder'

EditIdpSideModalForm.loader = async ({ params }: LoaderFunctionArgs) => {
export async function clientLoader({ params }: LoaderFunctionArgs) {
const { silo, provider } = getIdpSelector(params)
await apiQueryClient.prefetchQuery('samlIdentityProviderView', {
path: { provider },
Expand All @@ -30,7 +31,9 @@ EditIdpSideModalForm.loader = async ({ params }: LoaderFunctionArgs) => {
return null
}

export function EditIdpSideModalForm() {
export const handle = titleCrumb('Edit Identity Provider')

export default function EditIdpSideModalForm() {
const { silo, provider } = useIdpSelector()
const { data: idp } = usePrefetchedApiQuery('samlIdentityProviderView', {
path: { provider },
Expand Down
6 changes: 4 additions & 2 deletions app/forms/instance-create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ const baseDefaultValues: InstanceCreateInput = {
externalIps: [{ type: 'ephemeral' }],
}

CreateInstanceForm.loader = async ({ params }: LoaderFunctionArgs) => {
export async function clientLoader({ params }: LoaderFunctionArgs) {
const { project } = getProjectSelector(params)
await Promise.all([
// fetch both project and silo images
Expand All @@ -173,7 +173,9 @@ CreateInstanceForm.loader = async ({ params }: LoaderFunctionArgs) => {
return null
}

export function CreateInstanceForm() {
export const handle = { crumb: 'New instance' }

export default function CreateInstanceForm() {
const [isSubmitting, setIsSubmitting] = useState(false)
const queryClient = useApiQueryClient()
const { project } = useProjectSelector()
Expand Down
5 changes: 4 additions & 1 deletion app/forms/silo-create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { TextField } from '~/components/form/fields/TextField'
import { TlsCertsField } from '~/components/form/fields/TlsCertsField'
import { SideModalForm } from '~/components/form/SideModalForm'
import { HL } from '~/components/HL'
import { titleCrumb } from '~/hooks/use-crumbs'
import { addToast } from '~/stores/toast'
import { FormDivider } from '~/ui/lib/Divider'
import { FieldLabel } from '~/ui/lib/FieldLabel'
Expand Down Expand Up @@ -48,7 +49,9 @@ const defaultValues: SiloCreateFormValues = {
},
}

export function CreateSiloSideModalForm() {
export const handle = titleCrumb('New Silo')

export default function CreateSiloSideModalForm() {
const navigate = useNavigate()
const queryClient = useApiQueryClient()

Expand Down
7 changes: 5 additions & 2 deletions app/forms/ssh-key-edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,22 @@ import { DescriptionField } from '~/components/form/fields/DescriptionField'
import { NameField } from '~/components/form/fields/NameField'
import { TextField } from '~/components/form/fields/TextField'
import { SideModalForm } from '~/components/form/SideModalForm'
import { titleCrumb } from '~/hooks/use-crumbs'
import { getSshKeySelector, useSshKeySelector } from '~/hooks/use-params'
import { CopyToClipboard } from '~/ui/lib/CopyToClipboard'
import { PropertiesTable } from '~/ui/lib/PropertiesTable'
import { ResourceLabel } from '~/ui/lib/SideModal'
import { pb } from '~/util/path-builder'

EditSSHKeySideModalForm.loader = async ({ params }: LoaderFunctionArgs) => {
export async function clientLoader({ params }: LoaderFunctionArgs) {
const { sshKey } = getSshKeySelector(params)
await apiQueryClient.prefetchQuery('currentUserSshKeyView', { path: { sshKey } })
return null
}

export function EditSSHKeySideModalForm() {
export const handle = titleCrumb('View SSH Key')

export default function EditSSHKeySideModalForm() {
const navigate = useNavigate()
const { sshKey } = useSshKeySelector()

Expand Down
7 changes: 5 additions & 2 deletions app/forms/vpc-edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,24 @@ import { DescriptionField } from '~/components/form/fields/DescriptionField'
import { NameField } from '~/components/form/fields/NameField'
import { SideModalForm } from '~/components/form/SideModalForm'
import { HL } from '~/components/HL'
import { titleCrumb } from '~/hooks/use-crumbs'
import { getVpcSelector, useVpcSelector } from '~/hooks/use-params'
import { addToast } from '~/stores/toast'
import { pb } from '~/util/path-builder'
import type * as PP from '~/util/path-params'

export const handle = titleCrumb('Edit VPC')

const vpcView = ({ project, vpc }: PP.Vpc) =>
apiq('vpcView', { path: { vpc }, query: { project } })

EditVpcSideModalForm.loader = async ({ params }: LoaderFunctionArgs) => {
export async function clientLoader({ params }: LoaderFunctionArgs) {
const { project, vpc } = getVpcSelector(params)
await queryClient.prefetchQuery(vpcView({ project, vpc }))
return null
}

export function EditVpcSideModalForm() {
export default function EditVpcSideModalForm() {
const { vpc: vpcName, project } = useVpcSelector()
const navigate = useNavigate()

Expand Down
30 changes: 16 additions & 14 deletions app/layouts/AuthLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,19 @@ import { Outlet } from 'react-router'

import { OxideLogo } from '~/components/OxideLogo'

export const AuthLayout = () => (
<main
className="relative h-screen"
style={{
background:
'radial-gradient(200% 100% at 50% 100%, var(--surface-default) 0%, #161B1D 100%)',
}}
>
<OxideLogo className="absolute bottom-8 left-1/2 -translate-x-1/2" />
<div className="z-10 flex h-full items-center justify-center">
<Outlet />
</div>
</main>
)
export default function AuthLayout() {
return (
<main
className="relative h-screen"
style={{
background:
'radial-gradient(200% 100% at 50% 100%, var(--surface-default) 0%, #161B1D 100%)',
}}
>
<OxideLogo className="absolute bottom-8 left-1/2 -translate-x-1/2" />
<div className="z-10 flex h-full items-center justify-center">
<Outlet />
</div>
</main>
)
}
2 changes: 1 addition & 1 deletion app/layouts/LoginLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { Outlet } from 'react-router'
import heroRackImg from '~/assets/oxide-hero-rack.webp'
import { OxideLogo } from '~/components/OxideLogo'

export function LoginLayout() {
export default function LoginLayout() {
return (
<main className="layout relative flex h-screen">
<div className="hero-bg relative flex w-1/2 justify-end text-accent sm-:hidden">
Expand Down
5 changes: 4 additions & 1 deletion app/layouts/SettingsLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,17 @@ import { useLocation, useNavigate } from 'react-router'
import { Folder16Icon, Key16Icon, Profile16Icon } from '@oxide/design-system/icons/react'

import { TopBar } from '~/components/TopBar'
import { makeCrumb } from '~/hooks/use-crumbs'
import { useQuickActions } from '~/hooks/use-quick-actions'
import { Divider } from '~/ui/lib/Divider'
import { pb } from '~/util/path-builder'

import { DocsLinkItem, NavLinkItem, Sidebar } from '../components/Sidebar'
import { ContentPane, PageContainer } from './helpers'

export function SettingsLayout() {
export const handle = makeCrumb('Settings', pb.profile())

export default function SettingsLayout() {
const navigate = useNavigate()
const { pathname } = useLocation()

Expand Down
2 changes: 1 addition & 1 deletion app/layouts/SiloLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { pb } from '~/util/path-builder'

import { ContentPane, PageContainer } from './helpers'

export function SiloLayout() {
export default function SiloLayout() {
const navigate = useNavigate()
const { pathname } = useLocation()
const { me } = useCurrentUser()
Expand Down
5 changes: 2 additions & 3 deletions app/layouts/SystemLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import { ContentPane, PageContainer } from './helpers'
* error. We're being a little cavalier here with the error. If it's something
* other than a 403, that would be strange and we would want to know.
*/
export async function loader() {
export async function clientLoader() {
// we don't need to use the ErrorsAllowed version here because we're 404ing
// immediately on error, so we don't need to pick the result up from the cache
const isFleetViewer = await apiQueryClient
Expand All @@ -49,8 +49,7 @@ export async function loader() {
return null
}

Component.displayName = 'SystemLayout'
export function Component() {
export default function SystemLayout() {
// Only show silo picker if we are looking at a particular silo. The more
// robust way of doing this would be to make a separate layout for the
// silo-specific routes in the route config, but it's overkill considering
Expand Down
2 changes: 1 addition & 1 deletion app/pages/DeviceAuthSuccessPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { Success12Icon } from '@oxide/design-system/icons/react'
/**
* Device authorization success page
*/
export function DeviceAuthSuccessPage() {
export default function DeviceAuthSuccessPage() {
return (
<div className="flex w-full max-w-[470px] flex-col items-center rounded-lg border p-9 text-center !bg-raise border-secondary elevation-3">
<div className="my-2 flex h-12 w-12 items-center justify-center">
Expand Down
15 changes: 2 additions & 13 deletions app/pages/DeviceAuthVerifyPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,14 @@ import { Warning12Icon } from '@oxide/design-system/icons/react'
import { AuthCodeInput } from '~/ui/lib/AuthCodeInput'
import { Button } from '~/ui/lib/Button'
import { pb } from '~/util/path-builder'
import { addDashes } from '~/util/str'

const DASH_AFTER_IDXS = [3]

// nexus wants the dash. we plan on changing that so it doesn't care
export function addDashes(dashAfterIdxs: number[], code: string) {
let result = ''
for (let i = 0; i < code.length; i++) {
result += code[i]
if (dashAfterIdxs.includes(i)) {
result += '-'
}
}
return result
}

/**
* Device authorization verification page
*/
export function DeviceAuthVerifyPage() {
export default function DeviceAuthVerifyPage() {
const navigate = useNavigate()
const confirmPost = useApiMutation('deviceAuthConfirm', {
onSuccess: () => {
Expand Down
2 changes: 1 addition & 1 deletion app/pages/LoginPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const defaultValues: UsernamePasswordCredentials = {
}

/** Username/password form for local silo login */
export function LoginPage() {
export default function LoginPage() {
const [searchParams] = useSearchParams()
const navigate = useNavigate()
const { silo } = useSiloSelector()
Expand Down
Loading
Loading