mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-10-16 09:31:34 +08:00

* feat(organization): add hname and alias for organization * fix: boolean values are not shown in the list panel's column * fix: moved logic to component level * fix: added type * fix: added test cases * fix: added test cases * chore: update copy webpack plugin * Revert "fix: display same key with multiple data types in filter suggestions by enhancing the deduping logic (#7255)" This reverts commit 1e85981a17a8e715e948308d3e85072d976907d3. * fix: use query search v2 for traces data source to handle multiple data types for the same key * fix(QueryBuilderSearchV2): add user typed option if it doesn't exist in the payload * fix(QueryBuilderSearchV2): increase the height of search dropdown for non-logs data sources * fix: display span scope selector for trace data source * chore: remove the span scope selector from qb search v1 and move the component to search v2 * fix: write test to ensure that we display span scope selector for traces data source * fix: limit converting -> only to log data source * fix: don't display empty suggestion if only spaces are typed * chore: tests for span scope selector * chore: qb search flow (key, operator, value) test cases * refactor: fix the Maximum update depth reached issue while running tests * chore: overall improvements to span scope selector tests Resource attr filter: style fix and quick filter changes (#7691) * chore: resource attr filter init * chore: resource attr filter api integration * chore: operator config updated * chore: fliter show hide logic and styles * chore: add support for custom operator list to qb * chore: minor refactor * chore: minor code refactor * test: quick filters test suite added * test: quick filters test suite added * test: all errors test suite added * chore: style fix * test: all errors mock fix * chore: test case fix and mixpanel update * chore: color update * chore: minor refactor * chore: style fix * chore: set default query in exceptions tab * chore: style fix * chore: minor refactor * chore: minor refactor * chore: minor refactor * chore: test update * chore: fix filter header with no query name * fix: scroll fix * chore: add data source traces to quick filters * chore: replace div with fragment --------- Co-authored-by: Aditya Singh <adityasingh@Adityas-MacBook-Pro.local> fix: handle rate operators for table panel (#7695) * fix: handle rate operators for table panel chore: fix error rate (#7701) Signed-off-by: Shivanshu Raj Shrivastava <shivanshu1333@gmail.com> * feat(organization): minor cleanups * feat(organization): better naming for api and usecase * feat(organization): better packaging for modules * feat(organization): change hname to displayName * feat(organization): update the migration to use dialect * feat(organization): update the migration to use dialect * feat(organization): update the migration to use dialect * feat(organization): revert back to impl * feat(organization): remove DI from organization * feat(organization): address review comments * feat(organization): address review comments * feat(organization): address review comments --------- Signed-off-by: Shivanshu Raj Shrivastava <shivanshu1333@gmail.com>
364 lines
9.5 KiB
TypeScript
364 lines
9.5 KiB
TypeScript
/* eslint-disable sonarjs/cognitive-complexity */
|
|
import '../OnboardingQuestionaire.styles.scss';
|
|
|
|
import { Color } from '@signozhq/design-tokens';
|
|
import { Button, Input, Typography } from 'antd';
|
|
import logEvent from 'api/common/logEvent';
|
|
import editOrg from 'api/user/editOrg';
|
|
import { useNotifications } from 'hooks/useNotifications';
|
|
import { ArrowRight, CheckCircle, Loader2 } from 'lucide-react';
|
|
import { useAppContext } from 'providers/App/App';
|
|
import { useEffect, useState } from 'react';
|
|
import { useTranslation } from 'react-i18next';
|
|
|
|
export interface OrgData {
|
|
id: string;
|
|
displayName: string;
|
|
}
|
|
|
|
export interface OrgDetails {
|
|
organisationName: string;
|
|
usesObservability: boolean | null;
|
|
observabilityTool: string | null;
|
|
otherTool: string | null;
|
|
familiarity: string | null;
|
|
}
|
|
|
|
interface OrgQuestionsProps {
|
|
currentOrgData: OrgData | null;
|
|
orgDetails: OrgDetails;
|
|
onNext: (details: OrgDetails) => void;
|
|
}
|
|
|
|
const observabilityTools = {
|
|
AWSCloudwatch: 'AWS Cloudwatch',
|
|
DataDog: 'DataDog',
|
|
NewRelic: 'New Relic',
|
|
GrafanaPrometheus: 'Grafana / Prometheus',
|
|
AzureAppMonitor: 'Azure App Monitor',
|
|
GCPNativeO11yTools: 'GCP-native o11y tools',
|
|
Honeycomb: 'Honeycomb',
|
|
};
|
|
|
|
const o11yFamiliarityOptions: Record<string, string> = {
|
|
beginner: 'Beginner',
|
|
intermediate: 'Intermediate',
|
|
expert: 'Expert',
|
|
notFamiliar: "I'm not familiar with it",
|
|
};
|
|
|
|
function OrgQuestions({
|
|
currentOrgData,
|
|
orgDetails,
|
|
onNext,
|
|
}: OrgQuestionsProps): JSX.Element {
|
|
const { user, updateOrg } = useAppContext();
|
|
const { notifications } = useNotifications();
|
|
|
|
const { t } = useTranslation(['organizationsettings', 'common']);
|
|
|
|
const [organisationName, setOrganisationName] = useState<string>(
|
|
orgDetails?.organisationName || '',
|
|
);
|
|
const [usesObservability, setUsesObservability] = useState<boolean | null>(
|
|
orgDetails?.usesObservability || null,
|
|
);
|
|
const [observabilityTool, setObservabilityTool] = useState<string | null>(
|
|
orgDetails?.observabilityTool || null,
|
|
);
|
|
const [otherTool, setOtherTool] = useState<string>(
|
|
orgDetails?.otherTool || '',
|
|
);
|
|
const [familiarity, setFamiliarity] = useState<string | null>(
|
|
orgDetails?.familiarity || null,
|
|
);
|
|
const [isNextDisabled, setIsNextDisabled] = useState<boolean>(true);
|
|
|
|
useEffect(() => {
|
|
setOrganisationName(orgDetails.organisationName);
|
|
}, [orgDetails.organisationName]);
|
|
|
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
|
|
|
const handleOrgNameUpdate = async (): Promise<void> => {
|
|
/* Early bailout if orgData is not set or if the organisation name is not set or if the organisation name is empty or if the organisation name is the same as the one in the orgData */
|
|
if (
|
|
!currentOrgData ||
|
|
!organisationName ||
|
|
organisationName === '' ||
|
|
orgDetails.organisationName === organisationName
|
|
) {
|
|
logEvent('Org Onboarding: Answered', {
|
|
usesObservability,
|
|
observabilityTool,
|
|
otherTool,
|
|
familiarity,
|
|
});
|
|
|
|
onNext({
|
|
organisationName,
|
|
usesObservability,
|
|
observabilityTool,
|
|
otherTool,
|
|
familiarity,
|
|
});
|
|
|
|
return;
|
|
}
|
|
|
|
try {
|
|
setIsLoading(true);
|
|
const { statusCode, error } = await editOrg({
|
|
displayName: organisationName,
|
|
orgId: currentOrgData.id,
|
|
});
|
|
if (statusCode === 204) {
|
|
updateOrg(currentOrgData?.id, organisationName);
|
|
|
|
logEvent('Org Onboarding: Org Name Updated', {
|
|
organisationName,
|
|
});
|
|
|
|
logEvent('Org Onboarding: Answered', {
|
|
usesObservability,
|
|
observabilityTool,
|
|
otherTool,
|
|
familiarity,
|
|
});
|
|
|
|
onNext({
|
|
organisationName,
|
|
usesObservability,
|
|
observabilityTool,
|
|
otherTool,
|
|
familiarity,
|
|
});
|
|
} else {
|
|
logEvent('Org Onboarding: Org Name Update Failed', {
|
|
organisationName: orgDetails.organisationName,
|
|
});
|
|
|
|
notifications.error({
|
|
message:
|
|
error ||
|
|
t('something_went_wrong', {
|
|
ns: 'common',
|
|
}),
|
|
});
|
|
}
|
|
setIsLoading(false);
|
|
} catch (error) {
|
|
setIsLoading(false);
|
|
notifications.error({
|
|
message: t('something_went_wrong', {
|
|
ns: 'common',
|
|
}),
|
|
});
|
|
}
|
|
};
|
|
|
|
const isValidUsesObservability = (): boolean => {
|
|
if (usesObservability === null) {
|
|
return false;
|
|
}
|
|
|
|
if (usesObservability && (!observabilityTool || observabilityTool === '')) {
|
|
return false;
|
|
}
|
|
|
|
// eslint-disable-next-line sonarjs/prefer-single-boolean-return
|
|
if (usesObservability && observabilityTool === 'Others' && otherTool === '') {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
useEffect(() => {
|
|
const isValidObservability = isValidUsesObservability();
|
|
|
|
if (organisationName !== '' && familiarity !== null && isValidObservability) {
|
|
setIsNextDisabled(false);
|
|
} else {
|
|
setIsNextDisabled(true);
|
|
}
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [
|
|
organisationName,
|
|
usesObservability,
|
|
familiarity,
|
|
observabilityTool,
|
|
otherTool,
|
|
]);
|
|
|
|
const handleOnNext = (): void => {
|
|
handleOrgNameUpdate();
|
|
};
|
|
|
|
return (
|
|
<div className="questions-container">
|
|
<Typography.Title level={3} className="title">
|
|
Welcome, {user?.name}!
|
|
</Typography.Title>
|
|
<Typography.Paragraph className="sub-title">
|
|
We'll help you get the most out of SigNoz, whether you're new to
|
|
observability or a seasoned pro.
|
|
</Typography.Paragraph>
|
|
|
|
<div className="questions-form-container">
|
|
<div className="questions-form">
|
|
<div className="form-group">
|
|
<label className="question" htmlFor="organisationName">
|
|
Your Organisation Name
|
|
</label>
|
|
<input
|
|
type="text"
|
|
name="organisationName"
|
|
id="organisationName"
|
|
placeholder="For eg. Simpsonville..."
|
|
autoComplete="off"
|
|
value={organisationName}
|
|
onChange={(e): void => setOrganisationName(e.target.value)}
|
|
/>
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<label className="question" htmlFor="usesObservability">
|
|
Do you currently use any observability/monitoring tool?
|
|
</label>
|
|
|
|
<div className="two-column-grid">
|
|
<Button
|
|
type="primary"
|
|
name="usesObservability"
|
|
className={`onboarding-questionaire-button ${
|
|
usesObservability === true ? 'active' : ''
|
|
}`}
|
|
onClick={(): void => {
|
|
setUsesObservability(true);
|
|
}}
|
|
>
|
|
Yes{' '}
|
|
{usesObservability === true && (
|
|
<CheckCircle size={12} color={Color.BG_FOREST_500} />
|
|
)}
|
|
</Button>
|
|
<Button
|
|
type="primary"
|
|
className={`onboarding-questionaire-button ${
|
|
usesObservability === false ? 'active' : ''
|
|
}`}
|
|
onClick={(): void => {
|
|
setUsesObservability(false);
|
|
setObservabilityTool(null);
|
|
setOtherTool('');
|
|
}}
|
|
>
|
|
No{' '}
|
|
{usesObservability === false && (
|
|
<CheckCircle size={12} color={Color.BG_FOREST_500} />
|
|
)}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
{usesObservability && (
|
|
<div className="form-group">
|
|
<label className="question" htmlFor="observabilityTool">
|
|
Which observability tool do you currently use?
|
|
</label>
|
|
<div className="two-column-grid">
|
|
{Object.keys(observabilityTools).map((tool) => (
|
|
<Button
|
|
key={tool}
|
|
type="primary"
|
|
className={`onboarding-questionaire-button ${
|
|
observabilityTool === tool ? 'active' : ''
|
|
}`}
|
|
onClick={(): void => setObservabilityTool(tool)}
|
|
>
|
|
{observabilityTools[tool as keyof typeof observabilityTools]}
|
|
|
|
{observabilityTool === tool && (
|
|
<CheckCircle size={12} color={Color.BG_FOREST_500} />
|
|
)}
|
|
</Button>
|
|
))}
|
|
|
|
{observabilityTool === 'Others' ? (
|
|
<Input
|
|
type="text"
|
|
className="onboarding-questionaire-other-input"
|
|
placeholder="Please specify the tool"
|
|
value={otherTool || ''}
|
|
autoFocus
|
|
addonAfter={
|
|
otherTool && otherTool !== '' ? (
|
|
<CheckCircle size={12} color={Color.BG_FOREST_500} />
|
|
) : (
|
|
''
|
|
)
|
|
}
|
|
onChange={(e): void => setOtherTool(e.target.value)}
|
|
/>
|
|
) : (
|
|
<button
|
|
type="button"
|
|
className={`onboarding-questionaire-button ${
|
|
observabilityTool === 'Others' ? 'active' : ''
|
|
}`}
|
|
onClick={(): void => setObservabilityTool('Others')}
|
|
>
|
|
Others
|
|
</button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
<div className="form-group">
|
|
<div className="question">
|
|
Are you familiar with setting up observability (o11y)?
|
|
</div>
|
|
<div className="two-column-grid">
|
|
{Object.keys(o11yFamiliarityOptions).map((option: string) => (
|
|
<Button
|
|
key={option}
|
|
type="primary"
|
|
className={`onboarding-questionaire-button ${
|
|
familiarity === option ? 'active' : ''
|
|
}`}
|
|
onClick={(): void => setFamiliarity(option)}
|
|
>
|
|
{o11yFamiliarityOptions[option]}
|
|
{familiarity === option && (
|
|
<CheckCircle size={12} color={Color.BG_FOREST_500} />
|
|
)}
|
|
</Button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="next-prev-container">
|
|
<Button
|
|
type="primary"
|
|
className={`next-button ${isNextDisabled ? 'disabled' : ''}`}
|
|
onClick={handleOnNext}
|
|
disabled={isNextDisabled}
|
|
>
|
|
Next
|
|
{isLoading ? (
|
|
<Loader2 className="animate-spin" />
|
|
) : (
|
|
<ArrowRight size={14} />
|
|
)}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default OrgQuestions;
|