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
1 change: 1 addition & 0 deletions web/messages/en/components.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"cmp_nav_item_overview": "VPN Overview",
"cmp_nav_item_settings": "Settings",
"cmp_nav_item_edges": "Edge Components",
"cmp_nav_item_support": "Support",
"cmp_nav_item_activity_log": "Activity log",
"cmp_webhook_event_user_delete": "User deleted",
"cmp_webhook_event_user_add": "New user created",
Expand Down
25 changes: 25 additions & 0 deletions web/messages/en/support.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"$schema": "https://inlang.com/schema/inlang-message-format",
"support_page_title": "Support",
"support_page_subtitle": "When you need support or must supply diagnostic information to our team, please use one of the options below.",
"support_page_docs_title": "Have questions? Check our documentation first.",
"support_page_docs_desc": "Before contacting or submitting any issues to GitHub, please get familiar with Defguard documentation.",
"support_page_docs_btn": "Go to documentation",
"support_page_bug_title": "Report a bug",
"support_page_bug_desc_before": "We aim to respond to all bug reports as quickly as possible and prioritize them based on severity before adding them to our development backlog. To give us more context, ",
"support_page_bug_desc_bold": "you can optionally download",
"support_page_bug_desc_after": " the support data and/or log file and attach it to your bug report.",
"support_page_bug_btn_report": "Report on Github",
"support_page_bug_btn_download": "Download",
"support_page_bug_btn_download_support_data": "Download support data",
"support_page_bug_btn_download_logs": "Download logs",
"support_page_feature_title": "Request feature",
"support_page_feature_desc": "We grow with the help of our community. If you have an idea or a missing feature to suggest, please share it – we'll review it.",
"support_page_feature_btn": "Submit on Github",
"support_page_email_title": "Contact us by email",
"support_page_email_desc": "For any additional requests, reach out to us at",
"support_page_assistance_title": "Need Assistance?",
"support_page_assistance_desc": "Let us know your issue or arrange a meeting with our support team.",
"support_page_assistance_btn_ticket": "Open a support ticket",
"support_page_assistance_btn_call": "Schedule a call with our support team"
}
3 changes: 2 additions & 1 deletion web/project.inlang/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
"./messages/{locale}/initial_wizard.json",
"./messages/{locale}/migration_wizard.json",
"./messages/{locale}/api-error.json",
"./messages/{locale}/flow_end.json"
"./messages/{locale}/flow_end.json",
"./messages/{locale}/support.json"
]
}
}
190 changes: 190 additions & 0 deletions web/src/pages/SupportPage/SupportPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
import './styles.scss';
import { m } from '../../paraglide/messages';
import api from '../../shared/api/api';
import { Page } from '../../shared/components/Page/Page';
import { SettingsCard } from '../../shared/components/SettingsCard/SettingsCard';
import { SettingsHeader } from '../../shared/components/SettingsHeader/SettingsHeader';
import { SettingsLayout } from '../../shared/components/SettingsLayout/SettingsLayout';
import { externalLink } from '../../shared/constants';
import { AppText } from '../../shared/defguard-ui/components/AppText/AppText';
import { Button } from '../../shared/defguard-ui/components/Button/Button';
import { ButtonMenu } from '../../shared/defguard-ui/components/ButtonMenu/MenuButton';
import { ButtonsGroup } from '../../shared/defguard-ui/components/ButtonsGroup/ButtonsGroup';
import { Divider } from '../../shared/defguard-ui/components/Divider/Divider';
import { MarkedSection } from '../../shared/defguard-ui/components/MarkedSection/MarkedSection';
import { MarkedSectionHeader } from '../../shared/defguard-ui/components/MarkedSectionHeader/MarkedSectionHeader';
import docIllustration from '../../shared/defguard-ui/components/SectionSelect/assets/manual-user.png';
import { SizedBox } from '../../shared/defguard-ui/components/SizedBox/SizedBox';
import { TextStyle, ThemeSpacing, ThemeVariable } from '../../shared/defguard-ui/types';
import { downloadFile, downloadText } from '../../shared/utils/download';

export const SupportPage = () => {
return (
<Page title={m.support_page_title()}>
<SettingsLayout id="support-page-content">
<SettingsHeader
icon="support"
title={m.support_page_title()}
subtitle={m.support_page_subtitle()}
/>
<SettingsCard>
<MarkedSection icon="help">
<AppText font={TextStyle.TBodyPrimary600} color={ThemeVariable.FgDefault}>
{m.support_page_docs_title()}
</AppText>
<SizedBox height={ThemeSpacing.Xl} />
<div className="doc-highlight">
<img src={docIllustration} alt="" className="doc-highlight-illustration" />
<div className="doc-highlight-content">
<AppText font={TextStyle.TBodySm400} color={ThemeVariable.FgFaded}>
{m.support_page_docs_desc()}
</AppText>
<Button
variant="primary"
text={m.support_page_docs_btn()}
iconRight="open-in-new-window"
onClick={() =>
window.open(
externalLink.defguard.docs,
'_blank',
'noopener,noreferrer',
)
}
/>
</div>
</div>
</MarkedSection>
<Divider spacing={ThemeSpacing.Xl2} />
<MarkedSection icon="bug">
<AppText font={TextStyle.TBodyPrimary600} color={ThemeVariable.FgDefault}>
{m.support_page_bug_title()}
</AppText>
<SizedBox height={ThemeSpacing.Xl} />
<AppText font={TextStyle.TBodySm400} color={ThemeVariable.FgMuted}>
{m.support_page_bug_desc_before()}
<AppText
as="span"
font={TextStyle.TBodySm500}
color={ThemeVariable.FgMuted}
>
{m.support_page_bug_desc_bold()}
</AppText>
{m.support_page_bug_desc_after()}
</AppText>
<SizedBox height={ThemeSpacing.Xl} />
<ButtonsGroup>
<Button
variant="secondary"
text={m.support_page_bug_btn_report()}
iconLeft="github"
onClick={() =>
window.open(
externalLink.github.bugReport,
'_blank',
'noopener,noreferrer',
)
}
/>
<ButtonMenu
variant="outlined"
text={m.support_page_bug_btn_download()}
iconRight="arrow-small"
iconRightRotation="down"
menuItems={[
{
items: [
{
text: m.support_page_bug_btn_download_support_data(),
onClick: async () => {
const res = await api.support.getSupportData();
const blob = new Blob([JSON.stringify(res.data, null, 2)], {
type: 'application/json',
});
const now = new Date().toISOString().replace(/[:.]/g, '-');
downloadFile(blob, `defguard-support-data-${now}`, 'json');
},
},
{
text: m.support_page_bug_btn_download_logs(),
onClick: async () => {
const res = await api.support.getLogs();
const now = new Date().toISOString().replace(/[:.]/g, '-');
downloadText(res.data, `defguard-logs-${now}`, 'txt');
},
},
],
},
]}
/>
</ButtonsGroup>
</MarkedSection>
<Divider spacing={ThemeSpacing.Xl2} />
<MarkedSection icon="request">
<MarkedSectionHeader
title={m.support_page_feature_title()}
description={m.support_page_feature_desc()}
/>
<ButtonsGroup>
<Button
variant="secondary"
text={m.support_page_feature_btn()}
iconLeft="github"
onClick={() =>
window.open(
externalLink.github.featureRequest,
'_blank',
'noopener,noreferrer',
)
}
/>
</ButtonsGroup>
</MarkedSection>
<Divider spacing={ThemeSpacing.Xl2} />
<MarkedSection icon="mail">
<AppText font={TextStyle.TBodyPrimary600} color={ThemeVariable.FgDefault}>
{m.support_page_email_title()}
</AppText>
<SizedBox height={ThemeSpacing.Xl} />
<AppText font={TextStyle.TBodySm400} color={ThemeVariable.FgDefault}>
{m.support_page_email_desc()}{' '}
<a href="mailto:support@defguard.net">support@defguard.net</a>
</AppText>
</MarkedSection>
<Divider spacing={ThemeSpacing.Xl2} />
<MarkedSection icon="chat">
<MarkedSectionHeader
title={m.support_page_assistance_title()}
description={m.support_page_assistance_desc()}
/>
<ButtonsGroup>
<Button
variant="outlined"
text={m.support_page_assistance_btn_ticket()}
iconRight="open-in-new-window"
onClick={() =>
window.open(
externalLink.defguard.support,
'_blank',
'noopener,noreferrer',
)
}
/>
<Button
variant="outlined"
text={m.support_page_assistance_btn_call()}
iconRight="calendar"
onClick={() =>
window.open(
externalLink.defguard.scheduleCall,
'_blank',
'noopener,noreferrer',
)
}
/>
</ButtonsGroup>
</MarkedSection>
</SettingsCard>
</SettingsLayout>
</Page>
);
};
41 changes: 41 additions & 0 deletions web/src/pages/SupportPage/styles.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#support-page-content {
width: 784px;

a {
color: var(--fg-action);
text-decoration: underline;

&:hover {
text-decoration: none;
}
}

.doc-highlight {
background: var(--bg-action-muted);
border-radius: var(--radius-md);
padding: var(--spacing-xl);
display: flex;
align-items: center;
gap: var(--spacing-2xl);

.doc-highlight-illustration {
width: 80px;
height: auto;
flex-shrink: 0;
}

.doc-highlight-content {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: var(--spacing-lg);
flex: 1;

p {
margin: 0;
font: var(--t-body-sm-400);
color: var(--fg-muted);
}
}
}
}
22 changes: 22 additions & 0 deletions web/src/routeTree.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { Route as AuthorizedWizardAddLocationRouteImport } from './routes/_autho
import { Route as AuthorizedWizardAddExternalOpenidRouteImport } from './routes/_authorized/_wizard/add-external-openid'
import { Route as AuthorizedDefaultWebhooksRouteImport } from './routes/_authorized/_default/webhooks'
import { Route as AuthorizedDefaultUsersRouteImport } from './routes/_authorized/_default/users'
import { Route as AuthorizedDefaultSupportRouteImport } from './routes/_authorized/_default/support'
import { Route as AuthorizedDefaultOpenidRouteImport } from './routes/_authorized/_default/openid'
import { Route as AuthorizedDefaultNetworkDevicesRouteImport } from './routes/_authorized/_default/network-devices'
import { Route as AuthorizedDefaultGroupsRouteImport } from './routes/_authorized/_default/groups'
Expand Down Expand Up @@ -211,6 +212,12 @@ const AuthorizedDefaultUsersRoute = AuthorizedDefaultUsersRouteImport.update({
path: '/users',
getParentRoute: () => AuthorizedDefaultRoute,
} as any)
const AuthorizedDefaultSupportRoute =
AuthorizedDefaultSupportRouteImport.update({
id: '/support',
path: '/support',
getParentRoute: () => AuthorizedDefaultRoute,
} as any)
const AuthorizedDefaultOpenidRoute = AuthorizedDefaultOpenidRouteImport.update({
id: '/openid',
path: '/openid',
Expand Down Expand Up @@ -410,6 +417,7 @@ export interface FileRoutesByFullPath {
'/groups': typeof AuthorizedDefaultGroupsRoute
'/network-devices': typeof AuthorizedDefaultNetworkDevicesRoute
'/openid': typeof AuthorizedDefaultOpenidRoute
'/support': typeof AuthorizedDefaultSupportRoute
'/users': typeof AuthorizedDefaultUsersRoute
'/webhooks': typeof AuthorizedDefaultWebhooksRoute
'/add-external-openid': typeof AuthorizedWizardAddExternalOpenidRoute
Expand Down Expand Up @@ -467,6 +475,7 @@ export interface FileRoutesByTo {
'/groups': typeof AuthorizedDefaultGroupsRoute
'/network-devices': typeof AuthorizedDefaultNetworkDevicesRoute
'/openid': typeof AuthorizedDefaultOpenidRoute
'/support': typeof AuthorizedDefaultSupportRoute
'/users': typeof AuthorizedDefaultUsersRoute
'/webhooks': typeof AuthorizedDefaultWebhooksRoute
'/add-external-openid': typeof AuthorizedWizardAddExternalOpenidRoute
Expand Down Expand Up @@ -528,6 +537,7 @@ export interface FileRoutesById {
'/_authorized/_default/groups': typeof AuthorizedDefaultGroupsRoute
'/_authorized/_default/network-devices': typeof AuthorizedDefaultNetworkDevicesRoute
'/_authorized/_default/openid': typeof AuthorizedDefaultOpenidRoute
'/_authorized/_default/support': typeof AuthorizedDefaultSupportRoute
'/_authorized/_default/users': typeof AuthorizedDefaultUsersRoute
'/_authorized/_default/webhooks': typeof AuthorizedDefaultWebhooksRoute
'/_authorized/_wizard/add-external-openid': typeof AuthorizedWizardAddExternalOpenidRoute
Expand Down Expand Up @@ -588,6 +598,7 @@ export interface FileRouteTypes {
| '/groups'
| '/network-devices'
| '/openid'
| '/support'
| '/users'
| '/webhooks'
| '/add-external-openid'
Expand Down Expand Up @@ -645,6 +656,7 @@ export interface FileRouteTypes {
| '/groups'
| '/network-devices'
| '/openid'
| '/support'
| '/users'
| '/webhooks'
| '/add-external-openid'
Expand Down Expand Up @@ -705,6 +717,7 @@ export interface FileRouteTypes {
| '/_authorized/_default/groups'
| '/_authorized/_default/network-devices'
| '/_authorized/_default/openid'
| '/_authorized/_default/support'
| '/_authorized/_default/users'
| '/_authorized/_default/webhooks'
| '/_authorized/_wizard/add-external-openid'
Expand Down Expand Up @@ -956,6 +969,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof AuthorizedDefaultUsersRouteImport
parentRoute: typeof AuthorizedDefaultRoute
}
'/_authorized/_default/support': {
id: '/_authorized/_default/support'
path: '/support'
fullPath: '/support'
preLoaderRoute: typeof AuthorizedDefaultSupportRouteImport
parentRoute: typeof AuthorizedDefaultRoute
}
'/_authorized/_default/openid': {
id: '/_authorized/_default/openid'
path: '/openid'
Expand Down Expand Up @@ -1175,6 +1195,7 @@ interface AuthorizedDefaultRouteChildren {
AuthorizedDefaultGroupsRoute: typeof AuthorizedDefaultGroupsRoute
AuthorizedDefaultNetworkDevicesRoute: typeof AuthorizedDefaultNetworkDevicesRoute
AuthorizedDefaultOpenidRoute: typeof AuthorizedDefaultOpenidRoute
AuthorizedDefaultSupportRoute: typeof AuthorizedDefaultSupportRoute
AuthorizedDefaultUsersRoute: typeof AuthorizedDefaultUsersRoute
AuthorizedDefaultWebhooksRoute: typeof AuthorizedDefaultWebhooksRoute
AuthorizedDefaultAclAddAliasRoute: typeof AuthorizedDefaultAclAddAliasRoute
Expand Down Expand Up @@ -1210,6 +1231,7 @@ const AuthorizedDefaultRouteChildren: AuthorizedDefaultRouteChildren = {
AuthorizedDefaultGroupsRoute: AuthorizedDefaultGroupsRoute,
AuthorizedDefaultNetworkDevicesRoute: AuthorizedDefaultNetworkDevicesRoute,
AuthorizedDefaultOpenidRoute: AuthorizedDefaultOpenidRoute,
AuthorizedDefaultSupportRoute: AuthorizedDefaultSupportRoute,
AuthorizedDefaultUsersRoute: AuthorizedDefaultUsersRoute,
AuthorizedDefaultWebhooksRoute: AuthorizedDefaultWebhooksRoute,
AuthorizedDefaultAclAddAliasRoute: AuthorizedDefaultAclAddAliasRoute,
Expand Down
6 changes: 6 additions & 0 deletions web/src/routes/_authorized/_default/support.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { createFileRoute } from '@tanstack/react-router';
import { SupportPage } from '../../../pages/SupportPage/SupportPage';

export const Route = createFileRoute('/_authorized/_default/support')({
component: SupportPage,
});
4 changes: 4 additions & 0 deletions web/src/shared/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,10 @@ const api = {
fetchPage<ActivityLogEvent>(`/activity_log`, data),
info: () => client.get<ApplicationInfo>('/info'),
getLicenseInfo: () => client.get<LicenseInfoResponse>(`/enterprise_info`),
support: {
getSupportData: () => client.get<object>('/support/configuration'),
getLogs: () => client.get<string>('/support/logs'),
},
} as const;

export default api;
Loading
Loading