feat: add additional fields to first time setup trial flow (#11533)

* feat: add additional fields to first time setup trial flow

* trial generator typo
This commit is contained in:
Colin Adler 2024-01-16 18:19:16 -06:00 committed by GitHub
parent 1196f83ebd
commit be43d6247d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 1329 additions and 28 deletions

1
.gitattributes vendored
View File

@ -12,3 +12,4 @@ provisionersdk/proto/*.go linguist-generated=true
*.tfstate.dot linguist-generated=true
*.tfplan.dot linguist-generated=true
site/src/api/typesGenerated.ts linguist-generated=true
site/src/pages/SetupPage/countries.tsx linguist-generated=true

View File

@ -14,7 +14,7 @@ darcula = "darcula"
Hashi = "Hashi"
trialer = "trialer"
encrypter = "encrypter"
hel = "hel" # as in helsinki
hel = "hel" # as in helsinki
[files]
extend-exclude = [
@ -31,4 +31,5 @@ extend-exclude = [
"**/*.test.tsx",
"**/pnpm-lock.yaml",
"tailnet/testdata/**",
"site/src/pages/SetupPage/countries.tsx",
]

29
coderd/apidoc/docs.go generated
View File

@ -8261,6 +8261,9 @@ const docTemplate = `{
"trial": {
"type": "boolean"
},
"trial_info": {
"$ref": "#/definitions/codersdk.CreateFirstUserTrialInfo"
},
"username": {
"type": "string"
}
@ -8279,6 +8282,32 @@ const docTemplate = `{
}
}
},
"codersdk.CreateFirstUserTrialInfo": {
"type": "object",
"properties": {
"company_name": {
"type": "string"
},
"country": {
"type": "string"
},
"developers": {
"type": "string"
},
"first_name": {
"type": "string"
},
"job_title": {
"type": "string"
},
"last_name": {
"type": "string"
},
"phone_number": {
"type": "string"
}
}
},
"codersdk.CreateGroupRequest": {
"type": "object",
"properties": {

View File

@ -7353,6 +7353,9 @@
"trial": {
"type": "boolean"
},
"trial_info": {
"$ref": "#/definitions/codersdk.CreateFirstUserTrialInfo"
},
"username": {
"type": "string"
}
@ -7371,6 +7374,32 @@
}
}
},
"codersdk.CreateFirstUserTrialInfo": {
"type": "object",
"properties": {
"company_name": {
"type": "string"
},
"country": {
"type": "string"
},
"developers": {
"type": "string"
},
"first_name": {
"type": "string"
},
"job_title": {
"type": "string"
},
"last_name": {
"type": "string"
},
"phone_number": {
"type": "string"
}
}
},
"codersdk.CreateGroupRequest": {
"type": "object",
"properties": {

View File

@ -123,7 +123,7 @@ type Options struct {
TracerProvider trace.TracerProvider
ExternalAuthConfigs []*externalauth.Config
RealIPConfig *httpmw.RealIPConfig
TrialGenerator func(ctx context.Context, email string) error
TrialGenerator func(ctx context.Context, body codersdk.LicensorTrialRequest) error
// TLSCertificates is used to mesh DERP servers securely.
TLSCertificates []tls.Certificate
TailnetCoordinator tailnet.Coordinator

View File

@ -107,7 +107,7 @@ type Options struct {
Auditor audit.Auditor
TLSCertificates []tls.Certificate
ExternalAuthConfigs []*externalauth.Config
TrialGenerator func(context.Context, string) error
TrialGenerator func(ctx context.Context, body codersdk.LicensorTrialRequest) error
TemplateScheduleStore schedule.TemplateScheduleStore
Coordinator tailnet.Coordinator

View File

@ -152,7 +152,16 @@ func (api *API) postFirstUser(rw http.ResponseWriter, r *http.Request) {
}
if createUser.Trial && api.TrialGenerator != nil {
err = api.TrialGenerator(ctx, createUser.Email)
err = api.TrialGenerator(ctx, codersdk.LicensorTrialRequest{
Email: createUser.Email,
FirstName: createUser.TrialInfo.FirstName,
LastName: createUser.TrialInfo.LastName,
PhoneNumber: createUser.TrialInfo.PhoneNumber,
JobTitle: createUser.TrialInfo.JobTitle,
CompanyName: createUser.TrialInfo.CompanyName,
Country: createUser.TrialInfo.Country,
Developers: createUser.TrialInfo.Developers,
})
if err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Failed to generate trial",

View File

@ -76,7 +76,7 @@ func TestFirstUser(t *testing.T) {
t.Parallel()
called := make(chan struct{})
client := coderdtest.New(t, &coderdtest.Options{
TrialGenerator: func(ctx context.Context, s string) error {
TrialGenerator: func(context.Context, codersdk.LicensorTrialRequest) error {
close(called)
return nil
},

View File

@ -63,11 +63,38 @@ type GetUsersResponse struct {
Count int `json:"count"`
}
// @typescript-ignore LicensorTrialRequest
type LicensorTrialRequest struct {
DeploymentID string `json:"deployment_id"`
Email string `json:"email"`
Source string `json:"source"`
// Personal details.
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
PhoneNumber string `json:"phone_number"`
JobTitle string `json:"job_title"`
CompanyName string `json:"company_name"`
Country string `json:"country"`
Developers string `json:"developers"`
}
type CreateFirstUserRequest struct {
Email string `json:"email" validate:"required,email"`
Username string `json:"username" validate:"required,username"`
Password string `json:"password" validate:"required"`
Trial bool `json:"trial"`
Email string `json:"email" validate:"required,email"`
Username string `json:"username" validate:"required,username"`
Password string `json:"password" validate:"required"`
Trial bool `json:"trial"`
TrialInfo CreateFirstUserTrialInfo `json:"trial_info"`
}
type CreateFirstUserTrialInfo struct {
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
PhoneNumber string `json:"phone_number"`
JobTitle string `json:"job_title"`
CompanyName string `json:"company_name"`
Country string `json:"country"`
Developers string `json:"developers"`
}
// CreateFirstUserResponse contains IDs for newly created user info.

48
docs/api/schemas.md generated
View File

@ -1511,18 +1511,28 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
"email": "string",
"password": "string",
"trial": true,
"trial_info": {
"company_name": "string",
"country": "string",
"developers": "string",
"first_name": "string",
"job_title": "string",
"last_name": "string",
"phone_number": "string"
},
"username": "string"
}
```
### Properties
| Name | Type | Required | Restrictions | Description |
| ---------- | ------- | -------- | ------------ | ----------- |
| `email` | string | true | | |
| `password` | string | true | | |
| `trial` | boolean | false | | |
| `username` | string | true | | |
| Name | Type | Required | Restrictions | Description |
| ------------ | ---------------------------------------------------------------------- | -------- | ------------ | ----------- |
| `email` | string | true | | |
| `password` | string | true | | |
| `trial` | boolean | false | | |
| `trial_info` | [codersdk.CreateFirstUserTrialInfo](#codersdkcreatefirstusertrialinfo) | false | | |
| `username` | string | true | | |
## codersdk.CreateFirstUserResponse
@ -1540,6 +1550,32 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
| `organization_id` | string | false | | |
| `user_id` | string | false | | |
## codersdk.CreateFirstUserTrialInfo
```json
{
"company_name": "string",
"country": "string",
"developers": "string",
"first_name": "string",
"job_title": "string",
"last_name": "string",
"phone_number": "string"
}
```
### Properties
| Name | Type | Required | Restrictions | Description |
| -------------- | ------ | -------- | ------------ | ----------- |
| `company_name` | string | false | | |
| `country` | string | false | | |
| `developers` | string | false | | |
| `first_name` | string | false | | |
| `job_title` | string | false | | |
| `last_name` | string | false | | |
| `phone_number` | string | false | | |
## codersdk.CreateGroupRequest
```json

9
docs/api/users.md generated
View File

@ -226,6 +226,15 @@ curl -X POST http://coder-server:8080/api/v2/users/first \
"email": "string",
"password": "string",
"trial": true,
"trial_info": {
"company_name": "string",
"country": "string",
"developers": "string",
"first_name": "string",
"job_title": "string",
"last_name": "string",
"phone_number": "string"
},
"username": "string"
}
```

View File

@ -14,25 +14,19 @@ import (
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/database/dbtime"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/enterprise/coderd/license"
)
type request struct {
DeploymentID string `json:"deployment_id"`
Email string `json:"email"`
}
// New creates a handler that can issue trial licenses!
func New(db database.Store, url string, keys map[string]ed25519.PublicKey) func(ctx context.Context, email string) error {
return func(ctx context.Context, email string) error {
func New(db database.Store, url string, keys map[string]ed25519.PublicKey) func(ctx context.Context, body codersdk.LicensorTrialRequest) error {
return func(ctx context.Context, body codersdk.LicensorTrialRequest) error {
deploymentID, err := db.GetDeploymentID(ctx)
if err != nil {
return xerrors.Errorf("get deployment id: %w", err)
}
data, err := json.Marshal(request{
DeploymentID: deploymentID,
Email: email,
})
body.DeploymentID = deploymentID
data, err := json.Marshal(body)
if err != nil {
return xerrors.Errorf("marshal: %w", err)
}

View File

@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/coder/coder/v2/coderd/database/dbmem"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/enterprise/coderd/coderdenttest"
"github.com/coder/coder/v2/enterprise/trialer"
)
@ -26,7 +27,7 @@ func TestTrialer(t *testing.T) {
db := dbmem.New()
gen := trialer.New(db, srv.URL, coderdenttest.Keys)
err := gen(context.Background(), "kyle@coder.com")
err := gen(context.Background(), codersdk.LicensorTrialRequest{Email: "kyle+colin@coder.com"})
require.NoError(t, err)
licenses, err := db.GetLicenses(context.Background())
require.NoError(t, err)

View File

@ -183,6 +183,7 @@ export interface CreateFirstUserRequest {
readonly username: string;
readonly password: string;
readonly trial: boolean;
readonly trial_info: CreateFirstUserTrialInfo;
}
// From codersdk/users.go
@ -191,6 +192,17 @@ export interface CreateFirstUserResponse {
readonly organization_id: string;
}
// From codersdk/users.go
export interface CreateFirstUserTrialInfo {
readonly first_name: string;
readonly last_name: string;
readonly phone_number: string;
readonly job_title: string;
readonly company_name: string;
readonly country: string;
readonly developers: string;
}
// From codersdk/groups.go
export interface CreateGroupRequest {
readonly name: string;

View File

@ -15,6 +15,10 @@ import { docs } from "utils/docs";
import { SignInLayout } from "components/SignInLayout/SignInLayout";
import { FormFields, VerticalForm } from "components/Form/Form";
import { CoderIcon } from "components/Icons/CoderIcon";
import MenuItem from "@mui/material/MenuItem";
import { countries } from "./countries";
import Autocomplete from "@mui/material/Autocomplete";
import { Stack } from "components/Stack/Stack";
export const Language = {
emailLabel: "Email",
@ -25,6 +29,20 @@ export const Language = {
passwordRequired: "Please enter a password.",
create: "Create account",
welcomeMessage: <>Welcome to Coder</>,
firstNameLabel: "First name",
lastNameLabel: "Last name",
companyLabel: "Company",
jobTitleLabel: "Job title",
phoneNumberLabel: "Phone number",
countryLabel: "Country",
developersLabel: "Number of developers",
firstNameRequired: "Please enter your first name.",
phoneNumberRequired: "Please enter your phone number.",
jobTitleRequired: "Please enter your job title.",
companyNameRequired: "Please enter your company name.",
countryRequired: "Please select your country.",
developersRequired: "Please select the number of developers in your company.",
};
const validationSchema = Yup.object({
@ -34,8 +52,30 @@ const validationSchema = Yup.object({
.required(Language.emailRequired),
password: Yup.string().required(Language.passwordRequired),
username: nameValidator(Language.usernameLabel),
trial: Yup.bool(),
trial_info: Yup.object().when("trial", {
is: true,
then: (schema) =>
schema.shape({
first_name: Yup.string().required(Language.firstNameRequired),
last_name: Yup.string().required(Language.firstNameRequired),
phone_number: Yup.string().required(Language.phoneNumberRequired),
job_title: Yup.string().required(Language.jobTitleRequired),
company_name: Yup.string().required(Language.companyNameRequired),
country: Yup.string().required(Language.countryRequired),
developers: Yup.string().required(Language.developersRequired),
}),
}),
});
const numberOfDevelopersOptions = [
"1-100",
"101-500",
"501-1000",
"1001-2500",
"2500+",
];
export interface SetupPageViewProps {
onSubmit: (firstUser: TypesGen.CreateFirstUserRequest) => void;
error?: unknown;
@ -54,6 +94,15 @@ export const SetupPageView: FC<SetupPageViewProps> = ({
password: "",
username: "",
trial: false,
trial_info: {
first_name: "",
last_name: "",
phone_number: "",
job_title: "",
company_name: "",
country: "",
developers: "",
},
},
validationSchema,
onSubmit,
@ -161,6 +210,112 @@ export const SetupPageView: FC<SetupPageViewProps> = ({
</div>
</label>
{form.values.trial && (
<>
<Stack spacing={1.5} direction="row">
<TextField
{...getFieldHelpers("trial_info.first_name")}
id="trial_info.first_name"
name="trial_info.first_name"
fullWidth
label={Language.firstNameLabel}
/>
<TextField
{...getFieldHelpers("trial_info.last_name")}
id="trial_info.last_name"
name="trial_info.last_name"
fullWidth
label={Language.lastNameLabel}
/>
</Stack>
<TextField
{...getFieldHelpers("trial_info.company_name")}
id="trial_info.company_name"
name="trial_info.company_name"
fullWidth
label={Language.companyLabel}
/>
<TextField
{...getFieldHelpers("trial_info.job_title")}
id="trial_info.job_title"
name="trial_info.job_title"
fullWidth
label={Language.jobTitleLabel}
/>
<TextField
{...getFieldHelpers("trial_info.phone_number")}
id="trial_info.phone_number"
name="trial_info.phone_number"
fullWidth
label={Language.phoneNumberLabel}
/>
<Autocomplete
autoHighlight
options={countries}
renderOption={(props, country) => (
<li {...props}>{`${country.flag} ${country.name}`}</li>
)}
getOptionLabel={(option) => option.name}
onChange={(_, newValue) =>
form.setFieldValue("trial_info.country", newValue?.name)
}
css={{
"&:not(:has(label))": {
margin: 0,
},
}}
renderInput={(params) => (
<TextField
{...params}
{...getFieldHelpers("trial_info.country")}
id="trial_info.country"
name="trial_info.country"
label={Language.countryLabel}
fullWidth
inputProps={{
...params.inputProps,
}}
InputLabelProps={{ shrink: true }}
/>
)}
/>
<TextField
{...getFieldHelpers("trial_info.developers")}
id="trial_info.developers"
name="trial_info.developers"
fullWidth
label={Language.developersLabel}
select
>
{numberOfDevelopersOptions.map((opt) => (
<MenuItem key={opt} value={opt}>
{opt}
</MenuItem>
))}
</TextField>
<div
css={(theme) => ({
color: theme.palette.text.secondary,
fontSize: 11,
textAlign: "center",
marginTop: -5,
lineHeight: 1.5,
})}
>
Complete the form to receive your trial license and be contacted
about Coder products and solutions. The information you provide
will be treated in accordance with the{" "}
<Link
href="https://coder.com/legal/privacy-policy"
target="_blank"
>
Coder Privacy Policy
</Link>
. Opt-out at any time.
</div>
</>
)}
<LoadingButton
fullWidth
loading={isLoading}

998
site/src/pages/SetupPage/countries.tsx generated Normal file
View File

@ -0,0 +1,998 @@
export const countries = [
{
name: "Afghanistan",
flag: "🇦🇫",
},
{
name: "Åland Islands",
flag: "🇦🇽",
},
{
name: "Albania",
flag: "🇦🇱",
},
{
name: "Algeria",
flag: "🇩🇿",
},
{
name: "American Samoa",
flag: "🇦🇸",
},
{
name: "Andorra",
flag: "🇦🇩",
},
{
name: "Angola",
flag: "🇦🇴",
},
{
name: "Anguilla",
flag: "🇦🇮",
},
{
name: "Antarctica",
flag: "🇦🇶",
},
{
name: "Antigua and Barbuda",
flag: "🇦🇬",
},
{
name: "Argentina",
flag: "🇦🇷",
},
{
name: "Armenia",
flag: "🇦🇲",
},
{
name: "Aruba",
flag: "🇦🇼",
},
{
name: "Australia",
flag: "🇦🇺",
},
{
name: "Austria",
flag: "🇦🇹",
},
{
name: "Azerbaijan",
flag: "🇦🇿",
},
{
name: "Bahamas",
flag: "🇧🇸",
},
{
name: "Bahrain",
flag: "🇧🇭",
},
{
name: "Bangladesh",
flag: "🇧🇩",
},
{
name: "Barbados",
flag: "🇧🇧",
},
{
name: "Belarus",
flag: "🇧🇾",
},
{
name: "Belgium",
flag: "🇧🇪",
},
{
name: "Belize",
flag: "🇧🇿",
},
{
name: "Benin",
flag: "🇧🇯",
},
{
name: "Bermuda",
flag: "🇧🇲",
},
{
name: "Bhutan",
flag: "🇧🇹",
},
{
name: "Bolivia, Plurinational State of",
flag: "🇧🇴",
},
{
name: "Bonaire, Sint Eustatius and Saba",
flag: "🇧🇶",
},
{
name: "Bosnia and Herzegovina",
flag: "🇧🇦",
},
{
name: "Botswana",
flag: "🇧🇼",
},
{
name: "Bouvet Island",
flag: "🇧🇻",
},
{
name: "Brazil",
flag: "🇧🇷",
},
{
name: "British Indian Ocean Territory",
flag: "🇮🇴",
},
{
name: "Brunei Darussalam",
flag: "🇧🇳",
},
{
name: "Bulgaria",
flag: "🇧🇬",
},
{
name: "Burkina Faso",
flag: "🇧🇫",
},
{
name: "Burundi",
flag: "🇧🇮",
},
{
name: "Cambodia",
flag: "🇰🇭",
},
{
name: "Cameroon",
flag: "🇨🇲",
},
{
name: "Canada",
flag: "🇨🇦",
},
{
name: "Cape Verde",
flag: "🇨🇻",
},
{
name: "Cayman Islands",
flag: "🇰🇾",
},
{
name: "Central African Republic",
flag: "🇨🇫",
},
{
name: "Chad",
flag: "🇹🇩",
},
{
name: "Chile",
flag: "🇨🇱",
},
{
name: "China",
flag: "🇨🇳",
},
{
name: "Christmas Island",
flag: "🇨🇽",
},
{
name: "Cocos (Keeling) Islands",
flag: "🇨🇨",
},
{
name: "Colombia",
flag: "🇨🇴",
},
{
name: "Comoros",
flag: "🇰🇲",
},
{
name: "Congo",
flag: "🇨🇬",
},
{
name: "Congo, the Democratic Republic of the",
flag: "🇨🇩",
},
{
name: "Cook Islands",
flag: "🇨🇰",
},
{
name: "Costa Rica",
flag: "🇨🇷",
},
{
name: "Côte d'Ivoire",
flag: "🇨🇮",
},
{
name: "Croatia",
flag: "🇭🇷",
},
{
name: "Cuba",
flag: "🇨🇺",
},
{
name: "Curaçao",
flag: "🇨🇼",
},
{
name: "Cyprus",
flag: "🇨🇾",
},
{
name: "Czech Republic",
flag: "🇨🇿",
},
{
name: "Denmark",
flag: "🇩🇰",
},
{
name: "Djibouti",
flag: "🇩🇯",
},
{
name: "Dominica",
flag: "🇩🇲",
},
{
name: "Dominican Republic",
flag: "🇩🇴",
},
{
name: "Ecuador",
flag: "🇪🇨",
},
{
name: "Egypt",
flag: "🇪🇬",
},
{
name: "El Salvador",
flag: "🇸🇻",
},
{
name: "Equatorial Guinea",
flag: "🇬🇶",
},
{
name: "Eritrea",
flag: "🇪🇷",
},
{
name: "Estonia",
flag: "🇪🇪",
},
{
name: "Ethiopia",
flag: "🇪🇹",
},
{
name: "Falkland Islands (Malvinas)",
flag: "🇫🇰",
},
{
name: "Faroe Islands",
flag: "🇫🇴",
},
{
name: "Fiji",
flag: "🇫🇯",
},
{
name: "Finland",
flag: "🇫🇮",
},
{
name: "France",
flag: "🇫🇷",
},
{
name: "French Guiana",
flag: "🇬🇫",
},
{
name: "French Polynesia",
flag: "🇵🇫",
},
{
name: "French Southern Territories",
flag: "🇹🇫",
},
{
name: "Gabon",
flag: "🇬🇦",
},
{
name: "Gambia",
flag: "🇬🇲",
},
{
name: "Georgia",
flag: "🇬🇪",
},
{
name: "Germany",
flag: "🇩🇪",
},
{
name: "Ghana",
flag: "🇬🇭",
},
{
name: "Gibraltar",
flag: "🇬🇮",
},
{
name: "Greece",
flag: "🇬🇷",
},
{
name: "Greenland",
flag: "🇬🇱",
},
{
name: "Grenada",
flag: "🇬🇩",
},
{
name: "Guadeloupe",
flag: "🇬🇵",
},
{
name: "Guam",
flag: "🇬🇺",
},
{
name: "Guatemala",
flag: "🇬🇹",
},
{
name: "Guernsey",
flag: "🇬🇬",
},
{
name: "Guinea",
flag: "🇬🇳",
},
{
name: "Guinea-Bissau",
flag: "🇬🇼",
},
{
name: "Guyana",
flag: "🇬🇾",
},
{
name: "Haiti",
flag: "🇭🇹",
},
{
name: "Heard Island and McDonald Islands",
flag: "🇭🇲",
},
{
name: "Holy See (Vatican City State)",
flag: "🇻🇦",
},
{
name: "Honduras",
flag: "🇭🇳",
},
{
name: "Hong Kong",
flag: "🇭🇰",
},
{
name: "Hungary",
flag: "🇭🇺",
},
{
name: "Iceland",
flag: "🇮🇸",
},
{
name: "India",
flag: "🇮🇳",
},
{
name: "Indonesia",
flag: "🇮🇩",
},
{
name: "Iran, Islamic Republic of",
flag: "🇮🇷",
},
{
name: "Iraq",
flag: "🇮🇶",
},
{
name: "Ireland",
flag: "🇮🇪",
},
{
name: "Isle of Man",
flag: "🇮🇲",
},
{
name: "Israel",
flag: "🇮🇱",
},
{
name: "Italy",
flag: "🇮🇹",
},
{
name: "Jamaica",
flag: "🇯🇲",
},
{
name: "Japan",
flag: "🇯🇵",
},
{
name: "Jersey",
flag: "🇯🇪",
},
{
name: "Jordan",
flag: "🇯🇴",
},
{
name: "Kazakhstan",
flag: "🇰🇿",
},
{
name: "Kenya",
flag: "🇰🇪",
},
{
name: "Kiribati",
flag: "🇰🇮",
},
{
name: "Korea, Democratic People's Republic of",
flag: "🇰🇵",
},
{
name: "Korea, Republic of",
flag: "🇰🇷",
},
{
name: "Kuwait",
flag: "🇰🇼",
},
{
name: "Kyrgyzstan",
flag: "🇰🇬",
},
{
name: "Lao People's Democratic Republic",
flag: "🇱🇦",
},
{
name: "Latvia",
flag: "🇱🇻",
},
{
name: "Lebanon",
flag: "🇱🇧",
},
{
name: "Lesotho",
flag: "🇱🇸",
},
{
name: "Liberia",
flag: "🇱🇷",
},
{
name: "Libya",
flag: "🇱🇾",
},
{
name: "Liechtenstein",
flag: "🇱🇮",
},
{
name: "Lithuania",
flag: "🇱🇹",
},
{
name: "Luxembourg",
flag: "🇱🇺",
},
{
name: "Macao",
flag: "🇲🇴",
},
{
name: "Macedonia, the Former Yugoslav Republic of",
flag: "🇲🇰",
},
{
name: "Madagascar",
flag: "🇲🇬",
},
{
name: "Malawi",
flag: "🇲🇼",
},
{
name: "Malaysia",
flag: "🇲🇾",
},
{
name: "Maldives",
flag: "🇲🇻",
},
{
name: "Mali",
flag: "🇲🇱",
},
{
name: "Malta",
flag: "🇲🇹",
},
{
name: "Marshall Islands",
flag: "🇲🇭",
},
{
name: "Martinique",
flag: "🇲🇶",
},
{
name: "Mauritania",
flag: "🇲🇷",
},
{
name: "Mauritius",
flag: "🇲🇺",
},
{
name: "Mayotte",
flag: "🇾🇹",
},
{
name: "Mexico",
flag: "🇲🇽",
},
{
name: "Micronesia, Federated States of",
flag: "🇫🇲",
},
{
name: "Moldova, Republic of",
flag: "🇲🇩",
},
{
name: "Monaco",
flag: "🇲🇨",
},
{
name: "Mongolia",
flag: "🇲🇳",
},
{
name: "Montenegro",
flag: "🇲🇪",
},
{
name: "Montserrat",
flag: "🇲🇸",
},
{
name: "Morocco",
flag: "🇲🇦",
},
{
name: "Mozambique",
flag: "🇲🇿",
},
{
name: "Myanmar",
flag: "🇲🇲",
},
{
name: "Namibia",
flag: "🇳🇦",
},
{
name: "Nauru",
flag: "🇳🇷",
},
{
name: "Nepal",
flag: "🇳🇵",
},
{
name: "Netherlands",
flag: "🇳🇱",
},
{
name: "New Caledonia",
flag: "🇳🇨",
},
{
name: "New Zealand",
flag: "🇳🇿",
},
{
name: "Nicaragua",
flag: "🇳🇮",
},
{
name: "Niger",
flag: "🇳🇪",
},
{
name: "Nigeria",
flag: "🇳🇬",
},
{
name: "Niue",
flag: "🇳🇺",
},
{
name: "Norfolk Island",
flag: "🇳🇫",
},
{
name: "Northern Mariana Islands",
flag: "🇲🇵",
},
{
name: "Norway",
flag: "🇳🇴",
},
{
name: "Oman",
flag: "🇴🇲",
},
{
name: "Pakistan",
flag: "🇵🇰",
},
{
name: "Palau",
flag: "🇵🇼",
},
{
name: "Palestine, State of",
flag: "🇵🇸",
},
{
name: "Panama",
flag: "🇵🇦",
},
{
name: "Papua New Guinea",
flag: "🇵🇬",
},
{
name: "Paraguay",
flag: "🇵🇾",
},
{
name: "Peru",
flag: "🇵🇪",
},
{
name: "Philippines",
flag: "🇵🇭",
},
{
name: "Pitcairn",
flag: "🇵🇳",
},
{
name: "Poland",
flag: "🇵🇱",
},
{
name: "Portugal",
flag: "🇵🇹",
},
{
name: "Puerto Rico",
flag: "🇵🇷",
},
{
name: "Qatar",
flag: "🇶🇦",
},
{
name: "Réunion",
flag: "🇷🇪",
},
{
name: "Romania",
flag: "🇷🇴",
},
{
name: "Russian Federation",
flag: "🇷🇺",
},
{
name: "Rwanda",
flag: "🇷🇼",
},
{
name: "Saint Barthélemy",
flag: "🇧🇱",
},
{
name: "Saint Helena, Ascension and Tristan da Cunha",
flag: "🇸🇭",
},
{
name: "Saint Kitts and Nevis",
flag: "🇰🇳",
},
{
name: "Saint Lucia",
flag: "🇱🇨",
},
{
name: "Saint Martin (French part)",
flag: "🇲🇫",
},
{
name: "Saint Pierre and Miquelon",
flag: "🇵🇲",
},
{
name: "Saint Vincent and the Grenadines",
flag: "🇻🇨",
},
{
name: "Samoa",
flag: "🇼🇸",
},
{
name: "San Marino",
flag: "🇸🇲",
},
{
name: "Sao Tome and Principe",
flag: "🇸🇹",
},
{
name: "Saudi Arabia",
flag: "🇸🇦",
},
{
name: "Senegal",
flag: "🇸🇳",
},
{
name: "Serbia",
flag: "🇷🇸",
},
{
name: "Seychelles",
flag: "🇸🇨",
},
{
name: "Sierra Leone",
flag: "🇸🇱",
},
{
name: "Singapore",
flag: "🇸🇬",
},
{
name: "Sint Maarten (Dutch part)",
flag: "🇸🇽",
},
{
name: "Slovakia",
flag: "🇸🇰",
},
{
name: "Slovenia",
flag: "🇸🇮",
},
{
name: "Solomon Islands",
flag: "🇸🇧",
},
{
name: "Somalia",
flag: "🇸🇴",
},
{
name: "South Africa",
flag: "🇿🇦",
},
{
name: "South Georgia and the South Sandwich Islands",
flag: "🇬🇸",
},
{
name: "South Sudan",
flag: "🇸🇸",
},
{
name: "Spain",
flag: "🇪🇸",
},
{
name: "Sri Lanka",
flag: "🇱🇰",
},
{
name: "Sudan",
flag: "🇸🇩",
},
{
name: "Suriname",
flag: "🇸🇷",
},
{
name: "Svalbard and Jan Mayen",
flag: "🇸🇯",
},
{
name: "Swaziland",
flag: "🇸🇿",
},
{
name: "Sweden",
flag: "🇸🇪",
},
{
name: "Switzerland",
flag: "🇨🇭",
},
{
name: "Syrian Arab Republic",
flag: "🇸🇾",
},
{
name: "Taiwan, Province of China",
flag: "🇹🇼",
},
{
name: "Tajikistan",
flag: "🇹🇯",
},
{
name: "Tanzania, United Republic of",
flag: "🇹🇿",
},
{
name: "Thailand",
flag: "🇹🇭",
},
{
name: "Timor-Leste",
flag: "🇹🇱",
},
{
name: "Togo",
flag: "🇹🇬",
},
{
name: "Tokelau",
flag: "🇹🇰",
},
{
name: "Tonga",
flag: "🇹🇴",
},
{
name: "Trinidad and Tobago",
flag: "🇹🇹",
},
{
name: "Tunisia",
flag: "🇹🇳",
},
{
name: "Turkey",
flag: "🇹🇷",
},
{
name: "Turkmenistan",
flag: "🇹🇲",
},
{
name: "Turks and Caicos Islands",
flag: "🇹🇨",
},
{
name: "Tuvalu",
flag: "🇹🇻",
},
{
name: "Uganda",
flag: "🇺🇬",
},
{
name: "Ukraine",
flag: "🇺🇦",
},
{
name: "United Arab Emirates",
flag: "🇦🇪",
},
{
name: "United Kingdom",
flag: "🇬🇧",
},
{
name: "United States",
flag: "🇺🇸",
},
{
name: "United States Minor Outlying Islands",
flag: "🇺🇲",
},
{
name: "Uruguay",
flag: "🇺🇾",
},
{
name: "Uzbekistan",
flag: "🇺🇿",
},
{
name: "Vanuatu",
flag: "🇻🇺",
},
{
name: "Venezuela, Bolivarian Republic of",
flag: "🇻🇪",
},
{
name: "Viet Nam",
flag: "🇻🇳",
},
{
name: "Virgin Islands, British",
flag: "🇻🇬",
},
{
name: "Virgin Islands, U.S.",
flag: "🇻🇮",
},
{
name: "Wallis and Futuna",
flag: "🇼🇫",
},
{
name: "Western Sahara",
flag: "🇪🇭",
},
{
name: "Yemen",
flag: "🇾🇪",
},
{
name: "Zambia",
flag: "🇿🇲",
},
{
name: "Zimbabwe",
flag: "🇿🇼",
},
];

View File

@ -410,7 +410,7 @@ export const components = {
root: {
// Not sure why but since the input has padding we don't need it here
"& .MuiInputBase-root": {
padding: 0,
padding: "0px 0px 0px 8px",
},
},
},