refactor: migrate web Input to @makeplane/propel Input (#9708)

Replace @plane/ui Input in apps/web with Propel Input and InputGroup.
hasError maps to Field invalid. Default size is 2xl to match the old h-10 height.
This commit is contained in:
Anmol Singh Bhatia
2026-08-28 19:53:38 +05:30
committed by GitHub
parent 3478d4fac4
commit 4a6f9edff6
42 changed files with 1199 additions and 983 deletions

View File

@@ -7,10 +7,11 @@
import React, { useState } from "react";
import { observer } from "mobx-react";
// plane imports
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { DEFAULT_GLOBAL_VIEWS_LIST } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import { SearchIcon } from "@plane/propel/icons";
import { Input } from "@plane/ui";
// components
import { PageHead } from "@/components/core/page-title";
import { GlobalDefaultViewListItem } from "@/components/workspace/views/default-view-list-item";
@@ -30,15 +31,17 @@ function WorkspaceViewsPage() {
<>
<PageHead title={pageTitle} />
<div className="flex h-full w-full flex-col overflow-hidden">
<div className="flex h-11 w-full items-center gap-2.5 overflow-hidden border-b border-subtle px-5 py-3">
<SearchIcon className="text-secondary" width={14} height={14} strokeWidth={2} />
<Input
className="w-full bg-transparent !p-0 text-11 leading-5 text-secondary placeholder:text-placeholder focus:outline-none"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search"
mode="true-transparent"
/>
<div className="flex h-11 w-full items-center overflow-hidden border-b border-subtle px-5 py-3">
<InputGroup size="2xl">
<SearchIcon className="text-secondary" width={14} height={14} strokeWidth={2} />
<Input
size="2xl"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search"
aria-label="Search"
/>
</InputGroup>
</div>
<div className="vertical-scrollbar scrollbar-lg flex h-full w-full flex-col">
{DEFAULT_GLOBAL_VIEWS_LIST.filter((v) => t(v.i18n_label).toLowerCase().includes(query.toLowerCase())).map(

View File

@@ -10,11 +10,13 @@ import { observer } from "mobx-react";
// icons
import { CircleAlert, XCircle } from "lucide-react";
// plane imports
import { Field } from "@makeplane/propel/components/field";
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { useTranslation } from "@plane/i18n";
import { Button } from "@plane/propel/button";
import type { IEmailCheckData } from "@plane/types";
import { Input, Spinner } from "@plane/ui";
import { cn, checkEmailValidity } from "@plane/utils";
import { Spinner } from "@plane/ui";
import { checkEmailValidity } from "@plane/utils";
// helpers
type TAuthEmailForm = {
defaultEmail: string;
@@ -54,45 +56,44 @@ export const AuthEmailForm = observer(function AuthEmailForm(props: TAuthEmailFo
<label htmlFor="email" className="text-13 font-medium text-tertiary">
{t("auth.common.email.label")}
</label>
<div
className={cn(
`relative flex items-center rounded-md border bg-surface-1`,
!isFocused && Boolean(emailError?.email) ? `border-danger-strong` : `border-strong`
)}
onFocus={() => {
setIsFocused(true);
}}
onBlur={() => {
setIsFocused(false);
}}
>
<Input
id="email"
name="email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder={t("auth.common.email.placeholder")}
className={`h-10 w-full border-0 disable-autofill-style placeholder:text-placeholder autofill:bg-danger-primary focus:bg-none active:bg-transparent`}
autoComplete="off"
autoFocus
ref={inputRef}
/>
{email.length > 0 && (
<button
type="button"
onClick={() => {
setEmail("");
inputRef.current?.focus();
}}
className="absolute right-3 grid size-5 place-items-center"
aria-label={t("aria_labels.auth_forms.clear_email")}
tabIndex={-1}
>
<XCircle className="size-5 stroke-placeholder" />
</button>
)}
</div>
<Field name="email" invalid={!isFocused && Boolean(emailError?.email)}>
<InputGroup
size="2xl"
onFocus={() => {
setIsFocused(true);
}}
onBlur={() => {
setIsFocused(false);
}}
>
<Input
size="2xl"
id="email"
name="email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder={t("auth.common.email.placeholder")}
autoComplete="off"
autoFocus
ref={inputRef}
/>
{email.length > 0 && (
<button
type="button"
onClick={() => {
setEmail("");
inputRef.current?.focus();
}}
className="grid size-5 place-items-center"
aria-label={t("aria_labels.auth_forms.clear_email")}
tabIndex={-1}
>
<XCircle className="size-5 stroke-placeholder" />
</button>
)}
</InputGroup>
</Field>
{emailError?.email && !isFocused && (
<p className="flex items-center gap-1 px-0.5 text-11 text-danger-primary">
<CircleAlert height={12} width={12} />

View File

@@ -11,10 +11,12 @@ import { Controller, useForm } from "react-hook-form";
// icons
import { CircleCheck } from "lucide-react";
// plane imports
import { Field } from "@makeplane/propel/components/field";
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { useTranslation } from "@plane/i18n";
import { Button, getButtonStyling } from "@plane/propel/button";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import { Input } from "@plane/ui";
import { cn, checkEmailValidity } from "@plane/utils";
// hooks
import useTimer from "@/hooks/use-timer";
@@ -94,19 +96,22 @@ export const ForgotPasswordForm = observer(function ForgotPasswordForm() {
validate: (value) => checkEmailValidity(value) || t("auth.common.email.errors.invalid"),
}}
render={({ field: { value, onChange, ref } }) => (
<Input
id="email"
name="email"
type="email"
value={value}
onChange={onChange}
ref={ref}
hasError={Boolean(errors.email)}
placeholder={t("auth.common.email.placeholder")}
className="h-10 w-full border border-strong !bg-surface-1 pr-12 placeholder:text-placeholder"
autoComplete="off"
disabled={resendTimerCode > 0}
/>
<Field name="email" invalid={Boolean(errors.email)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="email"
name="email"
type="email"
value={value}
onChange={onChange}
ref={ref}
placeholder={t("auth.common.email.placeholder")}
autoComplete="off"
disabled={resendTimerCode > 0}
/>
</InputGroup>
</Field>
)}
/>
{resendTimerCode > 0 && (

View File

@@ -10,11 +10,12 @@ import Link from "next/link";
// icons
import { Eye, EyeOff, Info, XCircle } from "lucide-react";
// plane imports
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { API_BASE_URL, E_PASSWORD_STRENGTH } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import { Button } from "@plane/propel/button";
import { CloseIcon } from "@plane/propel/icons";
import { Input, PasswordStrengthIndicator, Spinner } from "@plane/ui";
import { PasswordStrengthIndicator, Spinner } from "@plane/ui";
import { getPasswordStrength } from "@plane/utils";
// components
import { ForgotPasswordPopover } from "@/components/account/auth-forms/forgot-password-popover";
@@ -173,43 +174,43 @@ export const AuthPasswordForm = observer(function AuthPasswordForm(props: Props)
<label htmlFor="email" className="text-13 font-medium text-tertiary">
{t("auth.common.email.label")}
</label>
<div className={`relative flex items-center rounded-md border border-strong bg-surface-1`}>
<InputGroup size="2xl">
<Input
size="2xl"
id="email"
name="email"
type="email"
value={passwordFormData.email}
onChange={(e) => handleFormChange("email", e.target.value)}
placeholder={t("auth.common.email.placeholder")}
className={`h-10 w-full border-0 disable-autofill-style placeholder:text-placeholder`}
disabled
/>
{passwordFormData.email.length > 0 && (
<button
type="button"
className="absolute right-3 size-5"
className="grid size-5 place-items-center"
onClick={handleEmailClear}
aria-label={t("aria_labels.auth_forms.clear_email")}
>
<XCircle className="size-5 stroke-placeholder" />
</button>
)}
</div>
</InputGroup>
</div>
<div className="space-y-1">
<label htmlFor="password" className="text-13 font-medium text-tertiary">
{mode === EAuthModes.SIGN_IN ? t("auth.common.password.label") : t("auth.common.password.set_password")}
</label>
<div className="relative flex items-center rounded-md bg-surface-1">
<InputGroup size="2xl">
<Input
size="2xl"
type={showPassword?.password ? "text" : "password"}
id="password"
name="password"
value={passwordFormData.password}
onChange={(e) => handleFormChange("password", e.target.value)}
placeholder={t("auth.common.password.placeholder")}
className="h-10 w-full border border-strong !bg-surface-1 pr-12 disable-autofill-style placeholder:text-placeholder"
onFocus={() => setIsPasswordInputFocused(true)}
onBlur={() => setIsPasswordInputFocused(false)}
autoComplete="off"
@@ -218,7 +219,7 @@ export const AuthPasswordForm = observer(function AuthPasswordForm(props: Props)
<button
type="button"
onClick={() => handleShowPassword("password")}
className="absolute right-3 grid size-5 place-items-center"
className="grid size-5 place-items-center"
aria-label={t(
showPassword?.password ? "aria_labels.auth_forms.hide_password" : "aria_labels.auth_forms.show_password"
)}
@@ -229,7 +230,7 @@ export const AuthPasswordForm = observer(function AuthPasswordForm(props: Props)
<Eye className="size-5 stroke-placeholder" />
)}
</button>
</div>
</InputGroup>
{passwordSupport}
</div>
@@ -238,22 +239,22 @@ export const AuthPasswordForm = observer(function AuthPasswordForm(props: Props)
<label htmlFor="confirm-password" className="text-13 font-medium text-tertiary">
{t("auth.common.password.confirm_password.label")}
</label>
<div className="relative flex items-center rounded-md bg-surface-1">
<InputGroup size="2xl">
<Input
size="2xl"
type={showPassword?.retypePassword ? "text" : "password"}
id="confirm-password"
name="confirm_password"
value={passwordFormData.confirm_password}
onChange={(e) => handleFormChange("confirm_password", e.target.value)}
placeholder={t("auth.common.password.confirm_password.placeholder")}
className="h-10 w-full border border-strong !bg-surface-1 pr-12 disable-autofill-style placeholder:text-placeholder"
onFocus={() => setIsRetryPasswordInputFocused(true)}
onBlur={() => setIsRetryPasswordInputFocused(false)}
autoComplete="off"
/>
<button
type="button"
className="absolute right-3 grid size-5 place-items-center"
className="grid size-5 place-items-center"
aria-label={t(
showPassword?.retypePassword
? "aria_labels.auth_forms.hide_password"
@@ -267,7 +268,7 @@ export const AuthPasswordForm = observer(function AuthPasswordForm(props: Props)
<Eye className="size-5 stroke-placeholder" />
)}
</button>
</div>
</InputGroup>
{!!passwordFormData.confirm_password &&
passwordFormData.password !== passwordFormData.confirm_password &&
renderPasswordMatchError && (

View File

@@ -10,10 +10,11 @@ import { useSearchParams } from "next/navigation";
// icons
import { Eye, EyeOff } from "lucide-react";
// ui
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { API_BASE_URL, E_PASSWORD_STRENGTH } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import { Button } from "@plane/propel/button";
import { Input, PasswordStrengthIndicator } from "@plane/ui";
import { PasswordStrengthIndicator } from "@plane/ui";
// components
import { getPasswordStrength } from "@plane/utils";
// helpers
@@ -114,81 +115,81 @@ export const ResetPasswordForm = observer(function ResetPasswordForm() {
<label className="text-13 font-medium text-tertiary" htmlFor="email">
{t("auth.common.email.label")}
</label>
<div className="relative flex items-center rounded-md bg-surface-1">
<InputGroup size="2xl">
<Input
size="2xl"
id="email"
name="email"
type="email"
value={resetFormData.email}
//hasError={Boolean(errors.email)}
placeholder={t("auth.common.email.placeholder")}
className="h-10 w-full cursor-not-allowed border border-strong !bg-surface-1 pr-12 text-placeholder"
autoComplete="off"
disabled
/>
</div>
</InputGroup>
</div>
<div className="space-y-1">
<label className="text-13 font-medium text-tertiary" htmlFor="password">
{t("auth.common.password.label")}
</label>
<div className="relative flex items-center rounded-md bg-surface-1">
<InputGroup size="2xl">
<Input
size="2xl"
type={showPassword.password ? "text" : "password"}
name="password"
id="password"
value={resetFormData.password}
onChange={(e) => handleFormChange("password", e.target.value)}
//hasError={Boolean(errors.password)}
placeholder={t("auth.common.password.placeholder")}
className="h-10 w-full border border-strong !bg-surface-1 pr-12 placeholder:text-placeholder"
minLength={8}
onFocus={() => setIsPasswordInputFocused(true)}
onBlur={() => setIsPasswordInputFocused(false)}
autoComplete="new-password"
autoFocus
/>
{showPassword.password ? (
<EyeOff
className="absolute right-3 h-5 w-5 stroke-placeholder hover:cursor-pointer"
onClick={() => handleShowPassword("password")}
/>
) : (
<Eye
className="absolute right-3 h-5 w-5 stroke-placeholder hover:cursor-pointer"
onClick={() => handleShowPassword("password")}
/>
)}
</div>
<button
type="button"
onClick={() => handleShowPassword("password")}
className="grid size-5 place-items-center"
>
{showPassword.password ? (
<EyeOff className="size-5 stroke-placeholder" />
) : (
<Eye className="size-5 stroke-placeholder" />
)}
</button>
</InputGroup>
<PasswordStrengthIndicator password={resetFormData.password} isFocused={isPasswordInputFocused} />
</div>
<div className="space-y-1">
<label className="text-13 font-medium text-tertiary" htmlFor="confirm_password">
{t("auth.common.password.confirm_password.label")}
</label>
<div className="relative flex items-center rounded-md bg-surface-1">
<InputGroup size="2xl">
<Input
size="2xl"
type={showPassword.retypePassword ? "text" : "password"}
name="confirm_password"
id="confirm_password"
value={resetFormData.confirm_password}
onChange={(e) => handleFormChange("confirm_password", e.target.value)}
placeholder={t("auth.common.password.confirm_password.placeholder")}
className="h-10 w-full border border-strong !bg-surface-1 pr-12 placeholder:text-placeholder"
onFocus={() => setIsRetryPasswordInputFocused(true)}
onBlur={() => setIsRetryPasswordInputFocused(false)}
autoComplete="new-password"
/>
{showPassword.retypePassword ? (
<EyeOff
className="absolute right-3 h-5 w-5 stroke-placeholder hover:cursor-pointer"
onClick={() => handleShowPassword("retypePassword")}
/>
) : (
<Eye
className="absolute right-3 h-5 w-5 stroke-placeholder hover:cursor-pointer"
onClick={() => handleShowPassword("retypePassword")}
/>
)}
</div>
<button
type="button"
onClick={() => handleShowPassword("retypePassword")}
className="grid size-5 place-items-center"
>
{showPassword.retypePassword ? (
<EyeOff className="size-5 stroke-placeholder" />
) : (
<Eye className="size-5 stroke-placeholder" />
)}
</button>
</InputGroup>
{!!resetFormData.confirm_password &&
resetFormData.password !== resetFormData.confirm_password &&
renderPasswordMatchError && (

View File

@@ -11,11 +11,12 @@ import { useSearchParams } from "next/navigation";
// icons
import { Eye, EyeOff } from "lucide-react";
// plane imports
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { E_PASSWORD_STRENGTH } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import { Button } from "@plane/propel/button";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import { Input, PasswordStrengthIndicator } from "@plane/ui";
import { PasswordStrengthIndicator } from "@plane/ui";
// components
import { getPasswordStrength } from "@plane/utils";
// hooks
@@ -117,81 +118,81 @@ export const SetPasswordForm = observer(function SetPasswordForm() {
<label className="text-13 font-medium text-tertiary" htmlFor="email">
{t("auth.common.email.label")}
</label>
<div className="relative flex items-center rounded-md bg-surface-1">
<InputGroup size="2xl">
<Input
size="2xl"
id="email"
name="email"
type="email"
value={user?.email}
//hasError={Boolean(errors.email)}
placeholder={t("auth.common.email.placeholder")}
className="h-10 w-full cursor-not-allowed border border-strong !bg-surface-1 pr-12 text-placeholder"
autoComplete="off"
disabled
/>
</div>
</InputGroup>
</div>
<div className="space-y-1">
<label className="text-13 font-medium text-tertiary" htmlFor="password">
{t("auth.common.password.label")}
</label>
<div className="relative flex items-center rounded-md bg-surface-1">
<InputGroup size="2xl">
<Input
size="2xl"
type={showPassword.password ? "text" : "password"}
name="password"
id="password"
value={passwordFormData.password}
onChange={(e) => handleFormChange("password", e.target.value)}
//hasError={Boolean(errors.password)}
placeholder={t("auth.common.password.placeholder")}
className="h-10 w-full border border-strong !bg-surface-1 pr-12 placeholder:text-placeholder"
minLength={8}
onFocus={() => setIsPasswordInputFocused(true)}
onBlur={() => setIsPasswordInputFocused(false)}
autoComplete="new-password"
autoFocus
/>
{showPassword.password ? (
<EyeOff
className="absolute right-3 h-5 w-5 stroke-placeholder hover:cursor-pointer"
onClick={() => handleShowPassword("password")}
/>
) : (
<Eye
className="absolute right-3 h-5 w-5 stroke-placeholder hover:cursor-pointer"
onClick={() => handleShowPassword("password")}
/>
)}
</div>
<button
type="button"
onClick={() => handleShowPassword("password")}
className="grid size-5 place-items-center"
>
{showPassword.password ? (
<EyeOff className="size-5 stroke-placeholder" />
) : (
<Eye className="size-5 stroke-placeholder" />
)}
</button>
</InputGroup>
<PasswordStrengthIndicator password={passwordFormData.password} isFocused={isPasswordInputFocused} />
</div>
<div className="space-y-1">
<label className="text-13 font-medium text-tertiary" htmlFor="confirm_password">
{t("auth.common.password.confirm_password.label")}
</label>
<div className="relative flex items-center rounded-md bg-surface-1">
<InputGroup size="2xl">
<Input
size="2xl"
type={showPassword.retypePassword ? "text" : "password"}
name="confirm_password"
id="confirm_password"
value={passwordFormData.confirm_password}
onChange={(e) => handleFormChange("confirm_password", e.target.value)}
placeholder={t("auth.common.password.confirm_password.placeholder")}
className="h-10 w-full border border-strong !bg-surface-1 pr-12 placeholder:text-placeholder"
onFocus={() => setIsRetryPasswordInputFocused(true)}
onBlur={() => setIsRetryPasswordInputFocused(false)}
autoComplete="new-password"
/>
{showPassword.retypePassword ? (
<EyeOff
className="absolute right-3 h-5 w-5 stroke-placeholder hover:cursor-pointer"
onClick={() => handleShowPassword("retypePassword")}
/>
) : (
<Eye
className="absolute right-3 h-5 w-5 stroke-placeholder hover:cursor-pointer"
onClick={() => handleShowPassword("retypePassword")}
/>
)}
</div>
<button
type="button"
onClick={() => handleShowPassword("retypePassword")}
className="grid size-5 place-items-center"
>
{showPassword.retypePassword ? (
<EyeOff className="size-5 stroke-placeholder" />
) : (
<Eye className="size-5 stroke-placeholder" />
)}
</button>
</InputGroup>
{!!passwordFormData.confirm_password &&
passwordFormData.password !== passwordFormData.confirm_password &&
renderPasswordMatchError && (

View File

@@ -6,10 +6,11 @@
import { useEffect, useState } from "react";
import { CircleCheck, XCircle } from "lucide-react";
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { API_BASE_URL } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import { Button } from "@plane/propel/button";
import { Input, Spinner } from "@plane/ui";
import { Spinner } from "@plane/ui";
// constants
// helpers
import { EAuthModes } from "@/helpers/authentication.helper";
@@ -98,45 +99,47 @@ export function AuthUniqueCodeForm(props: TAuthUniqueCodeForm) {
<label htmlFor="email" className="text-13 font-medium text-tertiary">
{t("auth.common.email.label")}
</label>
<div className={`relative flex items-center rounded-md border border-strong bg-surface-1`}>
<InputGroup size="2xl">
<Input
size="2xl"
id="email"
name="email"
type="email"
value={uniqueCodeFormData.email}
onChange={(e) => handleFormChange("email", e.target.value)}
placeholder={t("auth.common.email.placeholder")}
className="h-10 w-full border-0 disable-autofill-style placeholder:text-placeholder"
autoComplete="off"
disabled
/>
{uniqueCodeFormData.email.length > 0 && (
<button
type="button"
className="absolute right-3 grid size-5 place-items-center"
className="grid size-5 place-items-center"
aria-label={t("aria_labels.auth_forms.clear_email")}
onClick={handleEmailClear}
>
<XCircle className="size-5 stroke-placeholder" />
</button>
)}
</div>
</InputGroup>
</div>
<div className="space-y-1">
<label htmlFor="unique-code" className="text-13 font-medium text-tertiary">
{t("auth.common.unique_code.label")}
</label>
<Input
name="code"
id="unique-code"
value={uniqueCodeFormData.code}
onChange={(e) => handleFormChange("code", e.target.value)}
placeholder={t("auth.common.unique_code.placeholder")}
className="h-10 w-full border border-strong !bg-surface-1 pr-12 disable-autofill-style placeholder:text-placeholder"
autoComplete="off"
autoFocus
/>
<InputGroup size="2xl">
<Input
size="2xl"
name="code"
id="unique-code"
value={uniqueCodeFormData.code}
onChange={(e) => handleFormChange("code", e.target.value)}
placeholder={t("auth.common.unique_code.placeholder")}
autoComplete="off"
autoFocus
/>
</InputGroup>
<div className="flex w-full items-center justify-between px-1 pt-1 text-11">
<p className="flex items-center gap-1 font-medium text-success-primary">
<CircleCheck height={12} width={12} />

View File

@@ -9,13 +9,15 @@ import { add } from "date-fns";
import { Controller, useForm } from "react-hook-form";
import { Calendar } from "lucide-react";
// types
import { Field } from "@makeplane/propel/components/field";
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { useTranslation } from "@plane/i18n";
import { Button } from "@plane/propel/button";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import type { IApiToken } from "@plane/types";
// ui
import { Switch } from "@makeplane/propel/components/switch";
import { CustomSelect, Input, TextArea } from "@plane/ui";
import { CustomSelect, TextArea } from "@plane/ui";
import { cn, renderFormattedDate, renderFormattedTime } from "@plane/utils";
// components
import { DateDropdown } from "@/components/dropdowns/date";
@@ -144,14 +146,18 @@ export function CreateApiTokenForm(props: Props) {
validate: (val) => val.trim() !== "" || t("title_is_required"),
}}
render={({ field: { value, onChange } }) => (
<Input
type="text"
value={value}
onChange={onChange}
hasError={Boolean(errors.label)}
placeholder={t("title")}
className="w-full text-14"
/>
<Field name="input" invalid={Boolean(errors.label)}>
<InputGroup size="2xl">
<Input
size="2xl"
type="text"
value={value}
onChange={onChange}
placeholder={t("title")}
aria-label={t("title")}
/>
</InputGroup>
</Field>
)}
/>
{errors.label && <span className="text-11 text-danger-primary">{errors.label.message}</span>}

View File

@@ -7,11 +7,12 @@
import { useParams } from "next/navigation";
// react-hook-form
import { Controller, useForm } from "react-hook-form";
import { Field } from "@makeplane/propel/components/field";
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { Button } from "@plane/propel/button";
import type { IProject } from "@plane/types";
// ui
import { Input, EModalPosition, EModalWidth, ModalCore } from "@plane/ui";
import { EModalPosition, EModalWidth, ModalCore } from "@plane/ui";
// types
type Props = {
isOpen: boolean;
@@ -63,19 +64,22 @@ export function SelectMonthModal({ type, initialValues, isOpen, handleClose, han
}}
render={({ field: { value, onChange, ref } }) => (
<div className="relative flex w-full flex-col justify-center gap-1">
<Input
id="close_in"
name="close_in"
type="number"
value={value?.toString()}
onChange={onChange}
ref={ref}
hasError={Boolean(errors.close_in)}
placeholder="Enter Months"
className="w-full border-subtle"
min={1}
max={12}
/>
<Field name="close_in" invalid={Boolean(errors.close_in)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="close_in"
name="close_in"
type="number"
value={value?.toString()}
onChange={onChange}
ref={ref}
placeholder="Enter Months"
min={1}
max={12}
/>
</InputGroup>
</Field>
<span className="absolute top-2.5 right-8 text-13 text-secondary">Months</span>
</div>
)}
@@ -97,19 +101,22 @@ export function SelectMonthModal({ type, initialValues, isOpen, handleClose, han
}}
render={({ field: { value, onChange, ref } }) => (
<div className="relative flex w-full flex-col justify-center gap-1">
<Input
id="archive_in"
name="archive_in"
type="number"
value={value?.toString()}
onChange={onChange}
ref={ref}
hasError={Boolean(errors.archive_in)}
placeholder="Enter Months"
className="w-full border-subtle"
min={1}
max={12}
/>
<Field name="archive_in" invalid={Boolean(errors.archive_in)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="archive_in"
name="archive_in"
type="number"
value={value?.toString()}
onChange={onChange}
ref={ref}
placeholder="Enter Months"
min={1}
max={12}
/>
</InputGroup>
</Field>
<span className="absolute top-2.5 right-8 text-13 text-secondary">Months</span>
</div>
)}

View File

@@ -13,13 +13,14 @@ import { Controller } from "react-hook-form";
import useSWR from "swr";
import { Popover } from "@headlessui/react";
// plane imports
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { ACCEPTED_COVER_IMAGE_MIME_TYPES_FOR_REACT_DROPZONE, MAX_FILE_SIZE } from "@plane/constants";
import { useOutsideClickDetector } from "@plane/hooks";
import { Tabs } from "@plane/propel/tabs";
import { Button, getButtonStyling } from "@plane/propel/button";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import { EFileAssetType } from "@plane/types";
import { Input, Loader } from "@plane/ui";
import { Loader } from "@plane/ui";
// helpers
import { STATIC_COVER_IMAGES, getCoverImageDisplayURL } from "@/helpers/cover-image.helper";
// hooks
@@ -223,22 +224,24 @@ function ImagePickerPopoverComponent<TFieldValues extends FieldValues = FieldVal
control={control}
name={"search" as FieldPath<TFieldValues>}
render={({ field: { value, ref } }) => (
<Input
id="search"
name="search"
type="text"
onKeyDown={(e) => {
if (e.key === "Enter") {
e.preventDefault();
setSearchParams(formData.search);
}
}}
value={value}
onChange={(e) => setFormData({ ...formData, search: e.target.value })}
ref={ref}
placeholder="Search for images"
className="w-full text-13"
/>
<InputGroup size="2xl">
<Input
size="2xl"
id="search"
name="search"
type="text"
onKeyDown={(e) => {
if (e.key === "Enter") {
e.preventDefault();
setSearchParams(formData.search);
}
}}
value={value}
onChange={(e) => setFormData({ ...formData, search: e.target.value })}
ref={ref}
placeholder="Search for images"
/>
</InputGroup>
)}
/>
<Button variant="primary" size="xl" onClick={() => setSearchParams(formData.search)}>

View File

@@ -8,10 +8,12 @@ import { useState } from "react";
import { observer } from "mobx-react";
import { Controller, useForm } from "react-hook-form";
// plane imports
import { Field } from "@makeplane/propel/components/field";
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { useTranslation } from "@plane/i18n";
import { Button } from "@plane/propel/button";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import { Input, EModalPosition, EModalWidth, ModalCore } from "@plane/ui";
import { EModalPosition, EModalWidth, ModalCore } from "@plane/ui";
import { cn } from "@plane/utils";
// helpers
import { authErrorHandler } from "@/helpers/authentication.helper";
@@ -151,22 +153,22 @@ export const ChangeEmailModal = observer(function ChangeEmailModal(props: Props)
},
}}
render={({ field: { value, onChange, ref } }) => (
<Input
id="email"
name="email"
type="email"
value={value}
onChange={onChange}
ref={ref}
hasError={Boolean(errors.email)}
placeholder={changeEmailT("form.email.placeholder")}
className={cn(
{ "border-danger-strong": errors.email },
{ "cursor-not-allowed !bg-surface-2": secondStep }
)}
autoComplete="off"
disabled={secondStep}
/>
<Field name="email" invalid={Boolean(errors.email)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="email"
name="email"
type="email"
value={value}
onChange={onChange}
ref={ref}
placeholder={changeEmailT("form.email.placeholder")}
autoComplete="off"
disabled={secondStep}
/>
</InputGroup>
</Field>
)}
/>
{errors?.email && <span className="text-11 text-danger-primary">{errors?.email?.message}</span>}
@@ -180,17 +182,19 @@ export const ChangeEmailModal = observer(function ChangeEmailModal(props: Props)
name="code"
rules={{ required: changeEmailT("form.code.errors.required") }}
render={({ field: { value, onChange, ref } }) => (
<Input
id="code"
name="code"
value={value}
onChange={onChange}
ref={ref}
placeholder={changeEmailT("form.code.placeholder")}
className={cn({ "border-danger-strong": errors.code })}
autoComplete="off"
autoFocus
/>
<InputGroup size="2xl">
<Input
size="2xl"
id="code"
name="code"
value={value}
onChange={onChange}
ref={ref}
placeholder={changeEmailT("form.code.placeholder")}
autoComplete="off"
autoFocus
/>
</InputGroup>
)}
/>
{errors?.code ? (

View File

@@ -12,10 +12,11 @@ import { usePopper } from "react-popper";
import { AlertCircle } from "lucide-react";
import { Popover, Transition } from "@headlessui/react";
// plane imports
import { Input, InputGroup } from "@makeplane/propel/components/input";
import type { EditorRefApi } from "@plane/editor";
import { Button } from "@plane/propel/button";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import { Input } from "@plane/ui";
// components
import { RichTextEditor } from "@/components/editor/rich-text";
// services
@@ -260,19 +261,21 @@ export function GptAssistantPopover(props: Props) {
control={control}
name="task"
render={({ field: { value, onChange, ref } }) => (
<Input
id="task"
name="task"
type="text"
value={value}
onChange={onChange}
ref={ref}
placeholder={`${
prompt && prompt !== "" ? "Tell AI what action to perform on this content..." : "Ask AI anything..."
}`}
className="w-full"
autoFocus
/>
<InputGroup size="2xl">
<Input
size="2xl"
id="task"
name="task"
type="text"
value={value}
onChange={onChange}
ref={ref}
placeholder={`${
prompt && prompt !== "" ? "Tell AI what action to perform on this content..." : "Ask AI anything..."
}`}
autoFocus
/>
</InputGroup>
)}
/>
<div className="flex justify-between gap-2">

View File

@@ -7,13 +7,15 @@
import { useEffect } from "react";
import { Controller, useForm } from "react-hook-form";
// plane imports
import { Field } from "@makeplane/propel/components/field";
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { ETabIndices } from "@plane/constants";
// types
import { useTranslation } from "@plane/i18n";
import { Button } from "@plane/propel/button";
import type { ICycle } from "@plane/types";
// ui
import { Input, TextArea } from "@plane/ui";
import { TextArea } from "@plane/ui";
import { getDate, renderFormattedPayloadDate, getTabIndex } from "@plane/utils";
// components
import { DateRangeDropdown } from "@/components/dropdowns/date-range";
@@ -113,18 +115,21 @@ export function CycleForm(props: Props) {
},
}}
render={({ field: { value, onChange } }) => (
<Input
name="name"
type="text"
placeholder={t("title")}
className="w-full text-14"
value={value}
inputSize="md"
onChange={onChange}
hasError={Boolean(errors?.name)}
tabIndex={getIndex("description")}
autoFocus
/>
<Field name="name" invalid={Boolean(errors?.name)}>
<InputGroup size="2xl">
<Input
size="2xl"
name="name"
type="text"
placeholder={t("title")}
value={value}
onChange={onChange}
tabIndex={getIndex("description")}
autoFocus
aria-label={t("title")}
/>
</InputGroup>
</Field>
)}
/>
<span className="text-11 text-danger-primary">{errors?.name?.message}</span>

View File

@@ -9,10 +9,12 @@ import { observer } from "mobx-react";
import { Controller, useForm } from "react-hook-form";
// plane types
// plane ui
import { Field } from "@makeplane/propel/components/field";
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { useTranslation } from "@plane/i18n";
import { Button } from "@plane/propel/button";
import type { TLinkEditableFields } from "@plane/types";
import { Input, ModalCore } from "@plane/ui";
import { ModalCore } from "@plane/ui";
import type { TLinkOperations } from "./use-links";
export type TLinkOperationsModal = Exclude<TLinkOperations, "remove">;
@@ -87,16 +89,19 @@ export const LinkCreateUpdateModal = observer(function LinkCreateUpdateModal(pro
required: t("link.modal.url.required"),
}}
render={({ field: { value, onChange, ref } }) => (
<Input
id="url"
type="text"
value={value}
onChange={onChange}
ref={ref}
hasError={Boolean(errors.url)}
placeholder={t("link.modal.url.placeholder")}
className="w-full"
/>
<Field name="url" invalid={Boolean(errors.url)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="url"
type="text"
value={value}
onChange={onChange}
ref={ref}
placeholder={t("link.modal.url.placeholder")}
/>
</InputGroup>
</Field>
)}
/>
{errors.url && <span className="text-11 text-danger-primary">{t("link.modal.url.required")}</span>}
@@ -110,16 +115,19 @@ export const LinkCreateUpdateModal = observer(function LinkCreateUpdateModal(pro
control={control}
name="title"
render={({ field: { value, onChange, ref } }) => (
<Input
id="title"
type="text"
value={value}
onChange={onChange}
ref={ref}
hasError={Boolean(errors.title)}
placeholder={t("link.modal.title.placeholder")}
className="w-full"
/>
<Field name="title" invalid={Boolean(errors.title)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="title"
type="text"
value={value}
onChange={onChange}
ref={ref}
placeholder={t("link.modal.title.placeholder")}
/>
</InputGroup>
</Field>
)}
/>
</div>

View File

@@ -6,10 +6,11 @@
import { observer } from "mobx-react";
// plane imports
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { ETabIndices } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import type { TIssue } from "@plane/types";
import { Input } from "@plane/ui";
// helpers
import { getTabIndex } from "@plane/utils";
// hooks
@@ -30,17 +31,19 @@ export const InboxIssueTitle = observer(function InboxIssueTitle(props: TInboxIs
const { t } = useTranslation();
return (
<div className="space-y-1">
<Input
id="name"
name="name"
type="text"
value={data?.name}
onChange={(e) => handleData("name", e.target.value)}
placeholder={t("title")}
className="w-full text-14"
tabIndex={getIndex("name")}
required
/>
<InputGroup size="2xl">
<Input
size="2xl"
id="name"
name="name"
type="text"
value={data?.name}
onChange={(e) => handleData("name", e.target.value)}
placeholder={t("title")}
tabIndex={getIndex("name")}
required
/>
</InputGroup>
{isTitleLengthMoreThan255Character && (
<span className="text-11 text-danger-primary">{t("title_should_be_less_than_255_characters")}</span>
)}

View File

@@ -10,10 +10,12 @@ import { Controller, useForm } from "react-hook-form";
import { usePopper } from "react-popper";
import { Loader } from "lucide-react";
import { Popover } from "@headlessui/react";
import { Field } from "@makeplane/propel/components/field";
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { PlusIcon, CloseIcon } from "@plane/propel/icons";
import type { IIssueLabel } from "@plane/types";
// hooks
import { Input } from "@plane/ui";
// ui
// types
import type { TLabelOperations } from "./root";
@@ -134,18 +136,21 @@ export function LabelCreate(props: ILabelCreate) {
required: "This is required",
}}
render={({ field: { value, onChange, ref } }) => (
<Input
id="name"
name="name"
type="text"
value={value ?? ""}
onChange={onChange}
ref={ref}
hasError={Boolean(errors.name)}
placeholder="Title"
className="w-full px-1.5 py-1 text-11"
disabled={isSubmitting}
/>
<Field name="name" invalid={Boolean(errors.name)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="name"
name="name"
type="text"
value={value ?? ""}
onChange={onChange}
ref={ref}
placeholder="Title"
disabled={isSubmitting}
/>
</InputGroup>
</Field>
)}
/>
<button

View File

@@ -7,12 +7,14 @@
import { useEffect } from "react";
import { observer } from "mobx-react";
import { Controller, useForm } from "react-hook-form";
import { Field } from "@makeplane/propel/components/field";
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { useTranslation } from "@plane/i18n";
// plane types
import { Button } from "@plane/propel/button";
import type { TIssueLinkEditableFields, TIssueServiceType } from "@plane/types";
// plane ui
import { Input, ModalCore } from "@plane/ui";
import { ModalCore } from "@plane/ui";
// hooks
import { useIssueDetail } from "@/hooks/store/use-issue-detail";
// types
@@ -93,16 +95,19 @@ export const IssueLinkCreateUpdateModal = observer(function IssueLinkCreateUpdat
required: "URL is required",
}}
render={({ field: { value, onChange, ref } }) => (
<Input
id="url"
type="text"
value={value}
onChange={onChange}
ref={ref}
hasError={Boolean(errors.url)}
placeholder={t("common.type_or_paste_a_url")}
className="w-full"
/>
<Field name="url" invalid={Boolean(errors.url)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="url"
type="text"
value={value}
onChange={onChange}
ref={ref}
placeholder={t("common.type_or_paste_a_url")}
/>
</InputGroup>
</Field>
)}
/>
{errors.url && (
@@ -118,16 +123,19 @@ export const IssueLinkCreateUpdateModal = observer(function IssueLinkCreateUpdat
control={control}
name="title"
render={({ field: { value, onChange, ref } }) => (
<Input
id="title"
type="text"
value={value}
onChange={onChange}
ref={ref}
hasError={Boolean(errors.title)}
placeholder={t("common.link_title_placeholder")}
className="w-full"
/>
<Field name="title" invalid={Boolean(errors.title)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="title"
type="text"
value={value}
onChange={onChange}
ref={ref}
placeholder={t("common.link_title_placeholder")}
/>
</InputGroup>
</Field>
)}
/>
</div>

View File

@@ -9,12 +9,14 @@ import { observer } from "mobx-react";
import type { Control, FormState } from "react-hook-form";
import { Controller } from "react-hook-form";
// plane imports
import { Field } from "@makeplane/propel/components/field";
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { ETabIndices } from "@plane/constants";
// types
import { useTranslation } from "@plane/i18n";
import type { TIssue } from "@plane/types";
// ui
import { Input } from "@plane/ui";
// helpers
import { getTabIndex } from "@plane/utils";
// hooks
@@ -60,22 +62,25 @@ export const IssueTitleInput = observer(function IssueTitleInput(props: TIssueTi
},
}}
render={({ field: { value, onChange, ref } }) => (
<Input
id="name"
name="name"
type="text"
value={value}
onChange={(e) => {
onChange(e.target.value);
handleFormChange();
}}
ref={issueTitleRef || ref}
hasError={Boolean(errors.name)}
placeholder={t("title")}
className="w-full text-body-sm-regular"
autoFocus
tabIndex={getIndex("name")}
/>
<Field name="name" invalid={Boolean(errors.name)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="name"
name="name"
type="text"
value={value}
onChange={(e) => {
onChange(e.target.value);
handleFormChange();
}}
ref={issueTitleRef || ref}
placeholder={t("title")}
autoFocus
tabIndex={getIndex("name")}
/>
</InputGroup>
</Field>
)}
/>
<span className="text-caption-sm-medium text-danger-primary">{errors?.name?.message}</span>

View File

@@ -11,12 +11,13 @@ import type { SubmitHandler } from "react-hook-form";
import { Controller, useForm } from "react-hook-form";
import { Popover, Transition } from "@headlessui/react";
// plane imports
import { Field } from "@makeplane/propel/components/field";
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { getRandomLabelColor, LABEL_COLOR_OPTIONS } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import { Button } from "@plane/propel/button";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import type { IIssueLabel } from "@plane/types";
import { Input } from "@plane/ui";
// error codes
const errorCodes = {
@@ -219,18 +220,21 @@ export const CreateUpdateLabelInline = observer(
},
}}
render={({ field: { value, onChange, ref } }) => (
<Input
id="labelName"
name="name"
type="text"
autoFocus
value={value}
onChange={onChange}
ref={ref}
hasError={Boolean(errors.name)}
placeholder={t("project_settings.labels.label_title")}
className="w-full"
/>
<Field name="name" invalid={Boolean(errors.name)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="labelName"
name="name"
type="text"
autoFocus
value={value}
onChange={onChange}
ref={ref}
placeholder={t("project_settings.labels.label_title")}
/>
</InputGroup>
</Field>
)}
/>
</div>

View File

@@ -7,12 +7,14 @@
import { useEffect } from "react";
import { Controller, useForm } from "react-hook-form";
// plane imports
import { Field } from "@makeplane/propel/components/field";
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { ETabIndices } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import { Button } from "@plane/propel/button";
import type { IModule } from "@plane/types";
// ui
import { Input, TextArea } from "@plane/ui";
import { TextArea } from "@plane/ui";
import { getDate, renderFormattedPayloadDate, getTabIndex } from "@plane/utils";
// components
import { DateRangeDropdown } from "@/components/dropdowns/date-range";
@@ -124,18 +126,21 @@ export function ModuleForm(props: Props) {
},
}}
render={({ field: { value, onChange } }) => (
<Input
id="name"
name="name"
type="text"
value={value}
onChange={onChange}
hasError={Boolean(errors?.name)}
placeholder={t("title")}
className="w-full text-14"
tabIndex={getIndex("name")}
autoFocus
/>
<Field name="name" invalid={Boolean(errors?.name)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="name"
name="name"
type="text"
value={value}
onChange={onChange}
placeholder={t("title")}
tabIndex={getIndex("name")}
autoFocus
/>
</InputGroup>
</Field>
)}
/>
<span className="text-11 text-danger-primary">{errors?.name?.message}</span>

View File

@@ -7,12 +7,13 @@
import { useEffect } from "react";
import { Controller, useForm } from "react-hook-form";
// plane types
import { Field } from "@makeplane/propel/components/field";
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { Button } from "@plane/propel/button";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import type { ILinkDetails, ModuleLink } from "@plane/types";
// plane ui
import { Input, ModalCore } from "@plane/ui";
import { ModalCore } from "@plane/ui";
type Props = {
createLink: (formData: ModuleLink) => Promise<void>;
data?: ILinkDetails | null;
@@ -99,16 +100,19 @@ export function CreateUpdateModuleLinkModal(props: Props) {
required: "URL is required",
}}
render={({ field: { value, onChange, ref } }) => (
<Input
id="url"
type="text"
value={value}
onChange={onChange}
ref={ref}
hasError={Boolean(errors.url)}
placeholder="Type or paste a URL"
className="w-full"
/>
<Field name="url" invalid={Boolean(errors.url)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="url"
type="text"
value={value}
onChange={onChange}
ref={ref}
placeholder="Type or paste a URL"
/>
</InputGroup>
</Field>
)}
/>
</div>
@@ -121,16 +125,19 @@ export function CreateUpdateModuleLinkModal(props: Props) {
control={control}
name="title"
render={({ field: { value, onChange, ref } }) => (
<Input
id="title"
type="text"
value={value}
onChange={onChange}
ref={ref}
hasError={Boolean(errors.title)}
placeholder="What you'd like to see this link as"
className="w-full"
/>
<Field name="title" invalid={Boolean(errors.title)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="title"
type="text"
value={value}
onChange={onChange}
ref={ref}
placeholder="What you'd like to see this link as"
/>
</InputGroup>
</Field>
)}
/>
</div>

View File

@@ -8,6 +8,8 @@ import { useState } from "react";
import { observer } from "mobx-react";
import { Controller, useForm } from "react-hook-form";
// constants
import { Field } from "@makeplane/propel/components/field";
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { ORGANIZATION_SIZE, RESTRICTED_URLS } from "@plane/constants";
// types
import { useTranslation } from "@plane/i18n";
@@ -15,7 +17,7 @@ import { Button } from "@plane/propel/button";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import type { IUser, IWorkspace, TOnboardingSteps } from "@plane/types";
// ui
import { CustomSelect, Input, Spinner } from "@plane/ui";
import { CustomSelect, Spinner } from "@plane/ui";
import { validateWorkspaceName, validateSlug } from "@plane/utils";
// hooks
import { useWorkspace } from "@/hooks/store/use-workspace";
@@ -147,24 +149,27 @@ export const CreateWorkspace = observer(function CreateWorkspace(props: Props) {
}}
render={({ field: { value, ref, onChange } }) => (
<div className="relative flex items-center rounded-md">
<Input
id="name"
name="name"
type="text"
value={value}
onChange={(event) => {
onChange(event.target.value);
setValue("name", event.target.value);
setValue("slug", event.target.value.toLocaleLowerCase().trim().replace(/ /g, "-"), {
shouldValidate: true,
});
}}
placeholder={t("workspace_creation.form.name.placeholder")}
ref={ref}
hasError={Boolean(errors.name)}
className="w-full border-strong placeholder:text-placeholder"
autoFocus
/>
<Field name="name" invalid={Boolean(errors.name)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="name"
name="name"
type="text"
value={value}
onChange={(event) => {
onChange(event.target.value);
setValue("name", event.target.value);
setValue("slug", event.target.value.toLocaleLowerCase().trim().replace(/ /g, "-"), {
shouldValidate: true,
});
}}
placeholder={t("workspace_creation.form.name.placeholder")}
ref={ref}
autoFocus
/>
</InputGroup>
</Field>
</div>
)}
/>
@@ -188,29 +193,26 @@ export const CreateWorkspace = observer(function CreateWorkspace(props: Props) {
},
}}
render={({ field: { value, ref, onChange } }) => (
<div
className={`relative flex items-center rounded-md border-[0.5px] px-3 ${
invalidSlug ? "border-danger-strong" : "border-strong"
}`}
>
<span className="text-13 whitespace-nowrap">{window && window.location.host}/</span>
<Input
id="slug"
name="slug"
type="text"
value={value.toLocaleLowerCase().trim().replace(/ /g, "-")}
onChange={(e) => {
const validation = validateSlug(e.target.value);
if (validation === true) setInvalidSlug(false);
else setInvalidSlug(true);
onChange(e.target.value.toLowerCase());
}}
ref={ref}
hasError={Boolean(errors.slug)}
placeholder={t("workspace_creation.form.url.placeholder")}
className="w-full border-none !px-0"
/>
</div>
<Field name="slug" invalid={invalidSlug || Boolean(errors.slug)}>
<InputGroup size="2xl">
<span className="text-13 whitespace-nowrap">{window && window.location.host}/</span>
<Input
size="2xl"
id="slug"
name="slug"
type="text"
value={value.toLocaleLowerCase().trim().replace(/ /g, "-")}
onChange={(e) => {
const validation = validateSlug(e.target.value);
if (validation === true) setInvalidSlug(false);
else setInvalidSlug(true);
onChange(e.target.value.toLowerCase());
}}
ref={ref}
placeholder={t("workspace_creation.form.url.placeholder")}
/>
</InputGroup>
</Field>
)}
/>
<p className="text-13 text-tertiary">{t("workspace_creation.form.url.edit_slug")}</p>

View File

@@ -20,6 +20,8 @@ import { usePopper } from "react-popper";
import { XCircle } from "lucide-react";
import { Listbox } from "@headlessui/react";
// plane imports
import { Field } from "@makeplane/propel/components/field";
import { Input, InputGroup } from "@makeplane/propel/components/input";
import type { EUserPermissions } from "@plane/constants";
import { ROLE, ROLE_DETAILS } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
@@ -29,7 +31,7 @@ import { PlusIcon, CheckIcon, ChevronDownIcon } from "@plane/propel/icons";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import type { IUser, IWorkspace } from "@plane/types";
// ui
import { Input, Spinner } from "@plane/ui";
import { Spinner } from "@plane/ui";
// services
import { WorkspaceService } from "@/services/workspace.service";
// components
@@ -153,21 +155,24 @@ const InviteMemberInput = observer(function InviteMemberInput(props: InviteMembe
},
}}
render={({ field: { value, onChange, ref } }) => (
<Input
id={`emails.${index}.email`}
name={`emails.${index}.email`}
type="text"
value={value}
onChange={(event) => {
emailOnChange(event);
onChange(event);
}}
ref={ref}
hasError={Boolean(errors.emails?.[index]?.email)}
placeholder={placeholderEmails[index % placeholderEmails.length]}
className="w-full border-strong text-11 placeholder:text-placeholder sm:text-13"
autoComplete="off"
/>
<Field name="input" invalid={Boolean(errors.emails?.[index]?.email)}>
<InputGroup size="2xl">
<Input
size="2xl"
id={`emails.${index}.email`}
name={`emails.${index}.email`}
type="text"
value={value}
onChange={(event) => {
emailOnChange(event);
onChange(event);
}}
ref={ref}
placeholder={placeholderEmails[index % placeholderEmails.length]}
autoComplete="off"
/>
</InputGroup>
</Field>
)}
/>
</div>

View File

@@ -8,6 +8,8 @@ import { useMemo, useState } from "react";
import { observer } from "mobx-react";
import { Controller, useForm } from "react-hook-form";
import { Eye, EyeOff } from "lucide-react";
import { Field } from "@makeplane/propel/components/field";
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { E_PASSWORD_STRENGTH } from "@plane/constants";
// types
import { useTranslation } from "@plane/i18n";
@@ -15,7 +17,7 @@ import { Button } from "@plane/propel/button";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import type { IUser, TUserProfile, TOnboardingSteps } from "@plane/types";
// ui
import { Input, PasswordStrengthIndicator, Spinner } from "@plane/ui";
import { PasswordStrengthIndicator, Spinner } from "@plane/ui";
// components
import { cn, getFileURL, getPasswordStrength, validatePersonName } from "@plane/utils";
import { UserImageUploadModal } from "@/components/core/modals/user-image-upload-modal";
@@ -310,19 +312,22 @@ export const ProfileSetup = observer(function ProfileSetup(props: Props) {
},
}}
render={({ field: { value, onChange, ref } }) => (
<Input
id="first_name"
name="first_name"
type="text"
value={value}
autoFocus
onChange={onChange}
ref={ref}
hasError={Boolean(errors.first_name)}
placeholder="Wilbur"
className="w-full border-strong"
autoComplete="on"
/>
<Field name="first_name" invalid={Boolean(errors.first_name)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="first_name"
name="first_name"
type="text"
value={value}
autoFocus
onChange={onChange}
ref={ref}
placeholder="Wilbur"
autoComplete="on"
/>
</InputGroup>
</Field>
)}
/>
{errors.first_name && (
@@ -348,18 +353,21 @@ export const ProfileSetup = observer(function ProfileSetup(props: Props) {
},
}}
render={({ field: { value, onChange, ref } }) => (
<Input
id="last_name"
name="last_name"
type="text"
value={value}
onChange={onChange}
ref={ref}
hasError={Boolean(errors.last_name)}
placeholder="Wright"
className="w-full border-strong"
autoComplete="on"
/>
<Field name="last_name" invalid={Boolean(errors.last_name)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="last_name"
name="last_name"
type="text"
value={value}
onChange={onChange}
ref={ref}
placeholder="Wright"
autoComplete="on"
/>
</InputGroup>
</Field>
)}
/>
{errors.last_name && <span className="text-13 text-danger-primary">{errors.last_name.message}</span>}
@@ -380,32 +388,34 @@ export const ProfileSetup = observer(function ProfileSetup(props: Props) {
required: false,
}}
render={({ field: { value, onChange, ref } }) => (
<div className="relative flex items-center rounded-md">
<Input
type={showPassword.password ? "text" : "password"}
name="password"
value={value}
onChange={onChange}
ref={ref}
hasError={Boolean(errors.password)}
placeholder="New password..."
className="w-full border-[0.5px] border-subtle pr-12 placeholder:text-placeholder"
onFocus={() => setIsPasswordInputFocused(true)}
onBlur={() => setIsPasswordInputFocused(false)}
autoComplete="new-password"
/>
{showPassword.password ? (
<EyeOff
className="absolute right-3 h-4 w-4 stroke-placeholder hover:cursor-pointer"
onClick={() => handleShowPassword("password")}
<Field name="password" invalid={Boolean(errors.password)}>
<InputGroup size="2xl">
<Input
size="2xl"
type={showPassword.password ? "text" : "password"}
name="password"
value={value}
onChange={onChange}
ref={ref}
placeholder="New password..."
onFocus={() => setIsPasswordInputFocused(true)}
onBlur={() => setIsPasswordInputFocused(false)}
autoComplete="new-password"
aria-label="New password..."
/>
) : (
<Eye
className="absolute right-3 h-4 w-4 stroke-placeholder hover:cursor-pointer"
<button
type="button"
className="grid size-5 place-items-center"
onClick={() => handleShowPassword("password")}
/>
)}
</div>
>
{showPassword.password ? (
<EyeOff className="size-4 stroke-placeholder" />
) : (
<Eye className="size-4 stroke-placeholder" />
)}
</button>
</InputGroup>
</Field>
)}
/>
<PasswordStrengthIndicator password={watch("password") ?? ""} isFocused={isPasswordInputFocused} />
@@ -423,30 +433,32 @@ export const ProfileSetup = observer(function ProfileSetup(props: Props) {
watch("password") ? (value === watch("password") ? true : "Passwords don't match") : true,
}}
render={({ field: { value, onChange, ref } }) => (
<div className="relative flex items-center rounded-md">
<Input
type={showPassword.retypePassword ? "text" : "password"}
name="confirm_password"
value={value}
onChange={onChange}
ref={ref}
hasError={Boolean(errors.confirm_password)}
placeholder={t("auth.common.password.confirm_password.placeholder")}
className="w-full border-subtle pr-12 placeholder:text-placeholder"
autoComplete="new-password"
/>
{showPassword.retypePassword ? (
<EyeOff
className="absolute right-3 h-4 w-4 stroke-placeholder hover:cursor-pointer"
onClick={() => handleShowPassword("retypePassword")}
<Field name="confirm_password" invalid={Boolean(errors.confirm_password)}>
<InputGroup size="2xl">
<Input
size="2xl"
type={showPassword.retypePassword ? "text" : "password"}
name="confirm_password"
value={value}
onChange={onChange}
ref={ref}
placeholder={t("auth.common.password.confirm_password.placeholder")}
autoComplete="new-password"
aria-label={t("auth.common.password.confirm_password.placeholder")}
/>
) : (
<Eye
className="absolute right-3 h-4 w-4 stroke-placeholder hover:cursor-pointer"
<button
type="button"
className="grid size-5 place-items-center"
onClick={() => handleShowPassword("retypePassword")}
/>
)}
</div>
>
{showPassword.retypePassword ? (
<EyeOff className="size-4 stroke-placeholder" />
) : (
<Eye className="size-4 stroke-placeholder" />
)}
</button>
</InputGroup>
</Field>
)}
/>
{errors.confirm_password && (

View File

@@ -19,6 +19,8 @@ import { usePopper } from "react-popper";
import { XCircle } from "lucide-react";
import { Listbox } from "@headlessui/react";
// plane imports
import { Field } from "@makeplane/propel/components/field";
import { Input, InputGroup } from "@makeplane/propel/components/input";
import type { EUserPermissions } from "@plane/constants";
import { ROLE, ROLE_DETAILS } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
@@ -26,7 +28,7 @@ import { Button } from "@plane/propel/button";
import { PlusIcon, CheckIcon, ChevronDownIcon } from "@plane/propel/icons";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import { EOnboardingSteps } from "@plane/types";
import { Input, Spinner } from "@plane/ui";
import { Spinner } from "@plane/ui";
// hooks
import { useWorkspace } from "@/hooks/store/use-workspace";
// services
@@ -149,21 +151,24 @@ const InviteMemberInput = observer(function InviteMemberInput(props: InviteMembe
},
}}
render={({ field: { value, onChange, ref } }) => (
<Input
id={`emails.${index}.email`}
name={`emails.${index}.email`}
type="text"
value={value}
onChange={(event) => {
emailOnChange(event);
onChange(event);
}}
ref={ref}
hasError={Boolean(errors.emails?.[index]?.email)}
placeholder={placeholderEmails[index % placeholderEmails.length]}
className="w-full border-strong text-11 placeholder:text-placeholder sm:text-13"
autoComplete="off"
/>
<Field name="input" invalid={Boolean(errors.emails?.[index]?.email)}>
<InputGroup size="2xl">
<Input
size="2xl"
id={`emails.${index}.email`}
name={`emails.${index}.email`}
type="text"
value={value}
onChange={(event) => {
emailOnChange(event);
onChange(event);
}}
ref={ref}
placeholder={placeholderEmails[index % placeholderEmails.length]}
autoComplete="off"
/>
</InputGroup>
</Field>
)}
/>
</div>

View File

@@ -9,6 +9,7 @@ import { useState } from "react";
import type { LucideIcon } from "lucide-react";
// plane imports
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { ETabIndices, EPageAccess } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import { Button } from "@plane/propel/button";
@@ -16,7 +17,7 @@ import { EmojiPicker, EmojiIconPickerTypes, Logo } from "@plane/propel/emoji-ico
import { GlobeIcon, LockIcon, PageIcon } from "@plane/propel/icons";
import type { ISvgIcons } from "@plane/propel/icons";
import type { TPage } from "@plane/types";
import { Input } from "@plane/ui";
import { getTabIndex } from "@plane/utils";
// components
import { AccessField } from "@/components/common/access-field";
@@ -114,17 +115,19 @@ export function PageForm(props: Props) {
}
/>
<div className="flew-grow w-full space-y-1">
<Input
id="name"
type="text"
value={formData.name}
onChange={(e) => handleFormData("name", e.target.value)}
placeholder="Title"
className="w-full resize-none text-14"
tabIndex={getIndex("name")}
required
autoFocus
/>
<InputGroup size="2xl">
<Input
size="2xl"
id="name"
type="text"
value={formData.name}
onChange={(e) => handleFormData("name", e.target.value)}
placeholder="Title"
tabIndex={getIndex("name")}
required
autoFocus
/>
</InputGroup>
{isTitleLengthMoreThan255Character && (
<span className="text-11 text-danger-primary">
Max length of the name should be less than 255 characters

View File

@@ -7,9 +7,10 @@
import { useState, Fragment } from "react";
import { Dialog, Transition } from "@headlessui/react";
// plane imports
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { CloseIcon, SearchIcon } from "@plane/propel/icons";
import { ScrollArea } from "@plane/propel/scrollarea";
import { Input } from "@plane/ui";
// hooks
import { usePowerK } from "@/hooks/store/use-power-k";
// local imports
@@ -70,20 +71,21 @@ export function ShortcutsModal(props: Props) {
</button>
</Dialog.Title>
<div className="px-5">
<div className="flex w-full items-center rounded-sm border-[0.5px] border-subtle bg-surface-2 px-2">
<InputGroup size="2xl">
<SearchIcon className="h-3.5 w-3.5 text-secondary" />
<Input
size="2xl"
id="search"
name="search"
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search for shortcuts"
className="w-full border-none bg-transparent py-1 text-11 text-secondary outline-none"
aria-label="Search for shortcuts"
autoFocus
tabIndex={1}
/>
</div>
</InputGroup>
</div>
<ScrollArea size="sm" rootClassName="overflow-y-scroll px-5">

View File

@@ -6,10 +6,11 @@
import { useEffect, useState } from "react";
import { TwitterPicker } from "react-color";
import { Field } from "@makeplane/propel/components/field";
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { Button } from "@plane/propel/button";
import type { IState } from "@plane/types";
import { Popover, Input, TextArea } from "@plane/ui";
import { Popover, TextArea } from "@plane/ui";
type TStateForm = {
data: Partial<IState>;
onSubmit: (formData: Partial<IState>) => Promise<{ status: string }>;
@@ -73,18 +74,21 @@ export function StateForm(props: TStateForm) {
<div className="w-full space-y-2">
{/* title */}
<Input
id="name"
type="text"
name="name"
placeholder="Name"
value={formData?.name}
onChange={(e) => handleFormData("name", e.target.value)}
hasError={(errors && Boolean(errors.name)) || false}
className="w-full"
maxLength={100}
autoFocus
/>
<Field name="name" invalid={(errors && Boolean(errors.name)) || false}>
<InputGroup size="2xl">
<Input
size="2xl"
id="name"
type="text"
name="name"
placeholder="Name"
value={formData?.name}
onChange={(e) => handleFormData("name", e.target.value)}
maxLength={100}
autoFocus
/>
</InputGroup>
</Field>
{/* description */}
<TextArea

View File

@@ -7,6 +7,8 @@
import type { ChangeEvent } from "react";
import type { UseFormSetValue } from "react-hook-form";
import { Controller, useFormContext } from "react-hook-form";
import { Field } from "@makeplane/propel/components/field";
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { InfoIcon } from "@plane/propel/icons";
// plane imports
import { ETabIndices } from "@plane/constants";
@@ -14,7 +16,7 @@ import { useTranslation } from "@plane/i18n";
import type { TProject } from "@plane/types";
// ui
import { Tooltip } from "@plane/propel/tooltip";
import { Input, TextArea } from "@plane/ui";
import { TextArea } from "@plane/ui";
import { cn, projectIdentifierSanitizer, getTabIndex } from "@plane/utils";
type Props = {
@@ -68,17 +70,20 @@ function ProjectCommonAttributes(props: Props) {
},
}}
render={({ field: { value, onChange } }) => (
<Input
id="name"
name="name"
type="text"
value={value}
onChange={handleNameChange(onChange)}
hasError={Boolean(errors.name)}
placeholder={t("project_name")}
className="focus:border-blue-400 w-full"
tabIndex={getIndex("name")}
/>
<Field name="name" invalid={Boolean(errors.name)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="name"
name="name"
type="text"
value={value}
onChange={handleNameChange(onChange)}
placeholder={t("project_name")}
tabIndex={getIndex("name")}
/>
</InputGroup>
</Field>
)}
/>
<span className="text-11 text-danger-primary">{errors?.name?.message}</span>
@@ -102,19 +107,20 @@ function ProjectCommonAttributes(props: Props) {
},
}}
render={({ field: { value, onChange } }) => (
<Input
id="identifier"
name="identifier"
type="text"
value={value}
onChange={handleIdentifierChange(onChange)}
hasError={Boolean(errors.identifier)}
placeholder={t("project_id")}
className={cn("focus:border-blue-400 w-full pr-7 text-11", {
uppercase: value,
})}
tabIndex={getIndex("identifier")}
/>
<Field name="identifier" invalid={Boolean(errors.identifier)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="identifier"
name="identifier"
type="text"
value={value}
onChange={handleIdentifierChange(onChange)}
placeholder={t("project_id")}
tabIndex={getIndex("identifier")}
/>
</InputGroup>
</Field>
)}
/>
<Tooltip

View File

@@ -8,10 +8,12 @@ import { useParams } from "next/navigation";
import { Controller, useForm } from "react-hook-form";
import { AlertTriangle } from "lucide-react";
// Plane imports
import { Field } from "@makeplane/propel/components/field";
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { Button } from "@plane/propel/button";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import type { IProject } from "@plane/types";
import { Input, EModalPosition, EModalWidth, ModalCore } from "@plane/ui";
import { EModalPosition, EModalWidth, ModalCore } from "@plane/ui";
// hooks
import { useProject } from "@/hooks/store/use-project";
import { useAppRouter } from "@/hooks/use-app-router";
@@ -100,18 +102,21 @@ export function DeleteProjectModal(props: DeleteProjectModal) {
control={control}
name="projectName"
render={({ field: { value, onChange, ref } }) => (
<Input
id="projectName"
name="projectName"
type="text"
value={value}
onChange={onChange}
ref={ref}
hasError={Boolean(errors.projectName)}
placeholder="Project name"
className="mt-2 w-full"
autoComplete="off"
/>
<Field name="projectName" invalid={Boolean(errors.projectName)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="projectName"
name="projectName"
type="text"
value={value}
onChange={onChange}
ref={ref}
placeholder="Project name"
autoComplete="off"
/>
</InputGroup>
</Field>
)}
/>
</div>
@@ -123,18 +128,21 @@ export function DeleteProjectModal(props: DeleteProjectModal) {
control={control}
name="confirmDelete"
render={({ field: { value, onChange, ref } }) => (
<Input
id="confirmDelete"
name="confirmDelete"
type="text"
value={value}
onChange={onChange}
ref={ref}
hasError={Boolean(errors.confirmDelete)}
placeholder="Enter 'delete my project'"
className="mt-2 w-full"
autoComplete="off"
/>
<Field name="confirmDelete" invalid={Boolean(errors.confirmDelete)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="confirmDelete"
name="confirmDelete"
type="text"
value={value}
onChange={onChange}
ref={ref}
placeholder="Enter 'delete my project'"
autoComplete="off"
/>
</InputGroup>
</Field>
)}
/>
</div>

View File

@@ -7,6 +7,8 @@
import { useEffect, useState } from "react";
import { Controller, useForm } from "react-hook-form";
import { Info } from "lucide-react";
import { Field } from "@makeplane/propel/components/field";
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { NETWORK_CHOICES } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
// plane imports
@@ -17,7 +19,7 @@ import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import { Tooltip } from "@plane/propel/tooltip";
import { EFileAssetType } from "@plane/types";
import type { IProject, IWorkspace } from "@plane/types";
import { CustomSelect, Input, TextArea } from "@plane/ui";
import { CustomSelect, TextArea } from "@plane/ui";
import { renderFormattedDate } from "@plane/utils";
import { CoverImage } from "@/components/common/cover-image";
import { ImagePickerPopover } from "@/components/core/image-picker-popover";
@@ -287,18 +289,21 @@ export function ProjectDetailsForm(props: IProjectDetailsForm) {
},
}}
render={({ field: { value, onChange, ref } }) => (
<Input
id="name"
name="name"
type="text"
ref={ref}
value={value}
onChange={onChange}
hasError={Boolean(errors.name)}
className="rounded-md !p-3 font-medium"
placeholder={t("common.project_name")}
disabled={!isAdmin}
/>
<Field name="name" invalid={Boolean(errors.name)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="name"
name="name"
type="text"
ref={ref}
value={value}
onChange={onChange}
placeholder={t("common.project_name")}
disabled={!isAdmin}
/>
</InputGroup>
</Field>
)}
/>
<span className="text-11 text-danger-primary">{errors?.name?.message}</span>
@@ -342,18 +347,21 @@ export function ProjectDetailsForm(props: IProjectDetailsForm) {
},
}}
render={({ field: { value, ref } }) => (
<Input
id="identifier"
name="identifier"
type="text"
value={value}
onChange={handleIdentifierChange}
ref={ref}
hasError={Boolean(errors.identifier)}
placeholder={t("project_settings.general.enter_project_id")}
className="w-full font-medium"
disabled={!isAdmin}
/>
<Field name="identifier" invalid={Boolean(errors.identifier)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="identifier"
name="identifier"
type="text"
value={value}
onChange={handleIdentifierChange}
ref={ref}
placeholder={t("project_settings.general.enter_project_id")}
disabled={!isAdmin}
/>
</InputGroup>
</Field>
)}
/>
<Tooltip

View File

@@ -9,10 +9,12 @@ import { useParams } from "next/navigation";
import { Controller, useForm } from "react-hook-form";
import { AlertTriangleIcon } from "lucide-react";
// Plane imports
import { Field } from "@makeplane/propel/components/field";
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { Button } from "@plane/propel/button";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import type { IProject } from "@plane/types";
import { Input, EModalPosition, EModalWidth, ModalCore } from "@plane/ui";
import { EModalPosition, EModalWidth, ModalCore } from "@plane/ui";
// hooks
import { useUserPermissions } from "@/hooks/store/user";
import { useAppRouter } from "@/hooks/use-app-router";
@@ -125,17 +127,20 @@ export const LeaveProjectModal = observer(function LeaveProjectModal(props: ILea
required: "Label title is required",
}}
render={({ field: { value, onChange, ref } }) => (
<Input
id="projectName"
name="projectName"
type="text"
value={value}
onChange={onChange}
ref={ref}
hasError={Boolean(errors.projectName)}
placeholder="Enter project name"
className="mt-2 w-full"
/>
<Field name="projectName" invalid={Boolean(errors.projectName)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="projectName"
name="projectName"
type="text"
value={value}
onChange={onChange}
ref={ref}
placeholder="Enter project name"
/>
</InputGroup>
</Field>
)}
/>
</div>
@@ -148,17 +153,20 @@ export const LeaveProjectModal = observer(function LeaveProjectModal(props: ILea
control={control}
name="confirmLeave"
render={({ field: { value, onChange, ref } }) => (
<Input
id="confirmLeave"
name="confirmLeave"
type="text"
value={value}
onChange={onChange}
ref={ref}
hasError={Boolean(errors.confirmLeave)}
placeholder="Enter 'leave project'"
className="mt-2 w-full"
/>
<Field name="confirmLeave" invalid={Boolean(errors.confirmLeave)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="confirmLeave"
name="confirmLeave"
type="text"
value={value}
onChange={onChange}
ref={ref}
placeholder="Enter 'leave project'"
/>
</InputGroup>
</Field>
)}
/>
</div>

View File

@@ -9,12 +9,14 @@ import { observer } from "mobx-react";
import { Controller, useForm } from "react-hook-form";
import { CircleUserRound } from "lucide-react";
// plane imports
import { Field } from "@makeplane/propel/components/field";
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { useTranslation } from "@plane/i18n";
import { Button } from "@plane/propel/button";
import { TOAST_TYPE, setPromiseToast, setToast } from "@plane/propel/toast";
import { EFileAssetType } from "@plane/types";
import type { IUser, TUserProfile } from "@plane/types";
import { Input } from "@plane/ui";
import { getFileURL } from "@plane/utils";
// components
import { DeactivateAccountModal } from "@/components/account/deactivate-account-modal";
@@ -279,19 +281,22 @@ export const GeneralProfileSettingsForm = observer(function GeneralProfileSettin
validate: validatePersonName,
}}
render={({ field: { value, onChange, ref } }) => (
<Input
id="first_name"
name="first_name"
type="text"
value={value}
onChange={onChange}
ref={ref}
hasError={Boolean(errors.first_name)}
placeholder="Enter your first name"
className={`w-full rounded-md ${errors.first_name ? "border-danger-strong" : ""}`}
maxLength={50}
autoComplete="on"
/>
<Field name="first_name" invalid={Boolean(errors.first_name)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="first_name"
name="first_name"
type="text"
value={value}
onChange={onChange}
ref={ref}
placeholder="Enter your first name"
maxLength={50}
autoComplete="on"
/>
</InputGroup>
</Field>
)}
/>
{errors.first_name && <span className="text-11 text-danger-primary">{errors.first_name.message}</span>}
@@ -305,19 +310,22 @@ export const GeneralProfileSettingsForm = observer(function GeneralProfileSettin
validate: validatePersonName,
}}
render={({ field: { value, onChange, ref } }) => (
<Input
id="last_name"
name="last_name"
type="text"
value={value}
onChange={onChange}
ref={ref}
hasError={Boolean(errors.last_name)}
placeholder="Enter your last name"
className="w-full rounded-md"
maxLength={50}
autoComplete="on"
/>
<Field name="last_name" invalid={Boolean(errors.last_name)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="last_name"
name="last_name"
type="text"
value={value}
onChange={onChange}
ref={ref}
placeholder="Enter your last name"
maxLength={50}
autoComplete="on"
/>
</InputGroup>
</Field>
)}
/>
{errors.last_name && <span className="text-11 text-danger-primary">{errors.last_name.message}</span>}
@@ -335,18 +343,21 @@ export const GeneralProfileSettingsForm = observer(function GeneralProfileSettin
validate: validateDisplayName,
}}
render={({ field: { value, onChange, ref } }) => (
<Input
id="display_name"
name="display_name"
type="text"
value={value}
onChange={onChange}
ref={ref}
hasError={Boolean(errors?.display_name)}
placeholder="Enter your display name"
className={`w-full ${errors?.display_name ? "border-danger-strong" : ""}`}
maxLength={50}
/>
<Field name="display_name" invalid={Boolean(errors?.display_name)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="display_name"
name="display_name"
type="text"
value={value}
onChange={onChange}
ref={ref}
placeholder="Enter your display name"
maxLength={50}
/>
</InputGroup>
</Field>
)}
/>
{errors?.display_name && (
@@ -365,20 +376,21 @@ export const GeneralProfileSettingsForm = observer(function GeneralProfileSettin
required: "Email is required.",
}}
render={({ field: { value, ref } }) => (
<Input
id="email"
name="email"
type="email"
value={value}
ref={ref}
hasError={Boolean(errors.email)}
placeholder="Enter your email"
className={`w-full cursor-not-allowed rounded-md !bg-surface-2 ${
errors.email ? "border-danger-strong" : ""
}`}
autoComplete="on"
disabled
/>
<Field name="email" invalid={Boolean(errors.email)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="email"
name="email"
type="email"
value={value}
ref={ref}
placeholder="Enter your email"
autoComplete="on"
disabled
/>
</InputGroup>
</Field>
)}
/>
{isSMTPConfigured && (

View File

@@ -9,11 +9,13 @@ import { observer } from "mobx-react";
import { Controller, useForm } from "react-hook-form";
import { Eye, EyeOff } from "lucide-react";
// plane imports
import { Field } from "@makeplane/propel/components/field";
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { E_PASSWORD_STRENGTH } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import { Button } from "@plane/propel/button";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import { Input, PasswordStrengthIndicator } from "@plane/ui";
import { PasswordStrengthIndicator } from "@plane/ui";
import { getPasswordStrength } from "@plane/utils";
// components
import { ProfileSettingsHeading } from "@/components/settings/profile/heading";
@@ -138,38 +140,40 @@ export const SecurityProfileSettings = observer(function SecurityProfileSettings
{oldPasswordRequired && (
<div className="flex flex-col gap-y-2">
<h4 className="text-13">{t("auth.common.password.current_password.label")}</h4>
<div className="relative flex items-center rounded-md">
<Controller
control={control}
name="old_password"
rules={{
required: t("common.errors.required"),
}}
render={({ field: { value, onChange } }) => (
<Input
id="old_password"
type={showPassword?.oldPassword ? "text" : "password"}
value={value}
onChange={onChange}
placeholder={t("old_password")}
className="w-full"
hasError={Boolean(errors.old_password)}
autoComplete="current-password"
/>
)}
/>
{showPassword?.oldPassword ? (
<EyeOff
className="absolute right-3 h-5 w-5 stroke-placeholder hover:cursor-pointer"
onClick={() => handleShowPassword("oldPassword")}
/>
) : (
<Eye
className="absolute right-3 h-5 w-5 stroke-placeholder hover:cursor-pointer"
onClick={() => handleShowPassword("oldPassword")}
/>
<Controller
control={control}
name="old_password"
rules={{
required: t("common.errors.required"),
}}
render={({ field: { value, onChange } }) => (
<Field name="old_password" invalid={Boolean(errors.old_password)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="old_password"
type={showPassword?.oldPassword ? "text" : "password"}
value={value}
onChange={onChange}
placeholder={t("old_password")}
autoComplete="current-password"
/>
<button
type="button"
className="grid size-5 place-items-center"
onClick={() => handleShowPassword("oldPassword")}
aria-label={showPassword?.oldPassword ? "Hide password" : "Show password"}
>
{showPassword?.oldPassword ? (
<EyeOff className="size-5 stroke-placeholder" />
) : (
<Eye className="size-5 stroke-placeholder" />
)}
</button>
</InputGroup>
</Field>
)}
</div>
/>
{errors.old_password && (
<span className="text-11 text-danger-primary">{errors.old_password.message}</span>
)}
@@ -178,40 +182,42 @@ export const SecurityProfileSettings = observer(function SecurityProfileSettings
<div className="grid gap-x-4 gap-y-7 sm:grid-cols-2">
<div className="flex flex-col gap-y-2">
<h4 className="text-13">{t("auth.common.password.new_password.label")}</h4>
<div className="relative flex items-center rounded-md">
<Controller
control={control}
name="new_password"
rules={{
required: t("common.errors.required"),
}}
render={({ field: { value, onChange } }) => (
<Input
id="new_password"
type={showPassword?.password ? "text" : "password"}
value={value}
placeholder={t("auth.common.password.new_password.placeholder")}
onChange={onChange}
className="w-full"
hasError={Boolean(errors.new_password)}
onFocus={() => setIsPasswordInputFocused(true)}
onBlur={() => setIsPasswordInputFocused(false)}
autoComplete="new-password"
/>
)}
/>
{showPassword?.password ? (
<EyeOff
className="absolute right-3 h-5 w-5 stroke-placeholder hover:cursor-pointer"
onClick={() => handleShowPassword("password")}
/>
) : (
<Eye
className="absolute right-3 h-5 w-5 stroke-placeholder hover:cursor-pointer"
onClick={() => handleShowPassword("password")}
/>
<Controller
control={control}
name="new_password"
rules={{
required: t("common.errors.required"),
}}
render={({ field: { value, onChange } }) => (
<Field name="new_password" invalid={Boolean(errors.new_password)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="new_password"
type={showPassword?.password ? "text" : "password"}
value={value}
placeholder={t("auth.common.password.new_password.placeholder")}
onChange={onChange}
onFocus={() => setIsPasswordInputFocused(true)}
onBlur={() => setIsPasswordInputFocused(false)}
autoComplete="new-password"
/>
<button
type="button"
className="grid size-5 place-items-center"
onClick={() => handleShowPassword("password")}
aria-label={showPassword?.password ? "Hide password" : "Show password"}
>
{showPassword?.password ? (
<EyeOff className="size-5 stroke-placeholder" />
) : (
<Eye className="size-5 stroke-placeholder" />
)}
</button>
</InputGroup>
</Field>
)}
</div>
/>
{passwordSupport}
{errors.new_password && (
<span className="text-11 text-danger-primary">{errors.new_password.message}</span>
@@ -224,40 +230,42 @@ export const SecurityProfileSettings = observer(function SecurityProfileSettings
</div>
<div className="flex flex-col gap-y-2">
<h4 className="text-13">{t("auth.common.password.confirm_password.label")}</h4>
<div className="relative flex items-center rounded-md">
<Controller
control={control}
name="confirm_password"
rules={{
required: t("common.errors.required"),
}}
render={({ field: { value, onChange } }) => (
<Input
id="confirm_password"
type={showPassword?.confirmPassword ? "text" : "password"}
placeholder={t("auth.common.password.confirm_password.placeholder")}
value={value}
onChange={onChange}
className="w-full"
hasError={Boolean(errors.confirm_password)}
onFocus={() => setIsRetryPasswordInputFocused(true)}
onBlur={() => setIsRetryPasswordInputFocused(false)}
autoComplete="new-password"
/>
)}
/>
{showPassword?.confirmPassword ? (
<EyeOff
className="absolute right-3 h-5 w-5 stroke-placeholder hover:cursor-pointer"
onClick={() => handleShowPassword("confirmPassword")}
/>
) : (
<Eye
className="absolute right-3 h-5 w-5 stroke-placeholder hover:cursor-pointer"
onClick={() => handleShowPassword("confirmPassword")}
/>
<Controller
control={control}
name="confirm_password"
rules={{
required: t("common.errors.required"),
}}
render={({ field: { value, onChange } }) => (
<Field name="confirm_password" invalid={Boolean(errors.confirm_password)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="confirm_password"
type={showPassword?.confirmPassword ? "text" : "password"}
placeholder={t("auth.common.password.confirm_password.placeholder")}
value={value}
onChange={onChange}
onFocus={() => setIsRetryPasswordInputFocused(true)}
onBlur={() => setIsRetryPasswordInputFocused(false)}
autoComplete="new-password"
/>
<button
type="button"
className="grid size-5 place-items-center"
onClick={() => handleShowPassword("confirmPassword")}
aria-label={showPassword?.confirmPassword ? "Hide password" : "Show password"}
>
{showPassword?.confirmPassword ? (
<EyeOff className="size-5 stroke-placeholder" />
) : (
<Eye className="size-5 stroke-placeholder" />
)}
</button>
</InputGroup>
</Field>
)}
</div>
/>
{!!confirmPassword && password !== confirmPassword && renderPasswordMatchError && (
<span className="text-13 text-danger-primary">{t("auth.common.password.errors.match")}</span>
)}

View File

@@ -8,6 +8,8 @@ import { useState } from "react";
import { observer } from "mobx-react";
import { Controller, useForm } from "react-hook-form";
// plane imports
import { Field } from "@makeplane/propel/components/field";
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { ETabIndices, ISSUE_DISPLAY_FILTERS_BY_PAGE } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import { Button } from "@plane/propel/button";
@@ -21,7 +23,7 @@ import type {
IIssueFilters,
} from "@plane/types";
import { EViewAccess, EIssuesStoreType } from "@plane/types";
import { Input, TextArea } from "@plane/ui";
import { TextArea } from "@plane/ui";
import { getComputedDisplayFilters, getComputedDisplayProperties, getTabIndex } from "@plane/utils";
// components
import { DisplayFiltersSelection, FiltersDropdown } from "@/components/issues/issue-layouts/filters";
@@ -162,19 +164,22 @@ export const ProjectViewForm = observer(function ProjectViewForm(props: Props) {
},
}}
render={({ field: { value, onChange } }) => (
<Input
id="name"
type="name"
name="name"
value={value}
onChange={onChange}
hasError={Boolean(errors.name)}
placeholder={t("common.title")}
className="w-full text-14"
tabIndex={getIndex("name")}
// oxlint-disable-next-line jsx_a11y/no-autofocus
autoFocus
/>
<Field name="name" invalid={Boolean(errors.name)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="name"
type="name"
name="name"
value={value}
onChange={onChange}
placeholder={t("common.title")}
tabIndex={getIndex("name")}
// oxlint-disable-next-line jsx_a11y/no-autofocus
autoFocus
/>
</InputGroup>
</Field>
)}
/>
<span className="text-11 text-danger-primary">{errors?.name?.message?.toString()}</span>

View File

@@ -4,8 +4,9 @@
* See the LICENSE file for details.
*/
import { Field } from "@makeplane/propel/components/field";
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { useTranslation } from "@plane/i18n";
import { Input } from "@plane/ui";
type Props = {
value: string;
@@ -19,16 +20,20 @@ export function WebhookInput(props: Props) {
return (
<>
<h6 className="text-13 font-medium">{t("workspace_settings.settings.webhooks.modal.payload")}</h6>
<Input
type="url"
className="h-11 w-full"
onChange={(e) => onChange(e.target.value)}
value={value}
autoComplete="off"
hasError={hasError}
placeholder="https://example.com/post"
autoFocus
/>
<Field name="payload" invalid={hasError}>
<InputGroup size="2xl">
<Input
size="2xl"
type="url"
onChange={(e) => onChange(e.target.value)}
value={value}
autoComplete="off"
placeholder="https://example.com/post"
autoFocus
aria-label={t("workspace_settings.settings.webhooks.modal.payload")}
/>
</InputGroup>
</Field>
</>
);
}

View File

@@ -8,13 +8,15 @@ import type { Dispatch, SetStateAction } from "react";
import { useEffect, useState } from "react";
import { observer } from "mobx-react";
import { Controller, useForm } from "react-hook-form";
import { Field } from "@makeplane/propel/components/field";
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { ORGANIZATION_SIZE, RESTRICTED_URLS } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import { Button } from "@plane/propel/button";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import type { IWorkspace } from "@plane/types";
// ui
import { CustomSelect, Input } from "@plane/ui";
import { CustomSelect } from "@plane/ui";
import { validateWorkspaceName, validateSlug } from "@plane/utils";
// hooks
import { useWorkspace } from "@/hooks/store/use-workspace";
@@ -134,22 +136,25 @@ export const CreateWorkspaceForm = observer(function CreateWorkspaceForm(props:
},
}}
render={({ field: { value, ref, onChange } }) => (
<Input
id="workspaceName"
type="text"
value={value}
onChange={(e) => {
onChange(e.target.value);
setValue("name", e.target.value);
setValue("slug", e.target.value.toLocaleLowerCase().trim().replace(/ /g, "-"), {
shouldValidate: true,
});
}}
ref={ref}
hasError={Boolean(errors.name)}
placeholder={t("workspace_creation.form.name.placeholder")}
className="w-full"
/>
<Field name="workspaceName" invalid={Boolean(errors.name)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="workspaceName"
type="text"
value={value}
onChange={(e) => {
onChange(e.target.value);
setValue("name", e.target.value);
setValue("slug", e.target.value.toLocaleLowerCase().trim().replace(/ /g, "-"), {
shouldValidate: true,
});
}}
ref={ref}
placeholder={t("workspace_creation.form.name.placeholder")}
/>
</InputGroup>
</Field>
)}
/>
<span className="text-11 text-danger-primary">{errors?.name?.message}</span>
@@ -160,37 +165,38 @@ export const CreateWorkspaceForm = observer(function CreateWorkspaceForm(props:
{t("workspace_creation.form.url.label")}
<span className="ml-0.5 text-danger-primary">*</span>
</label>
<div className="flex w-full items-center rounded-md border border-subtle bg-layer-2 px-3">
<span className="text-12 whitespace-nowrap text-secondary">{window && window.location.host}/</span>
<Controller
control={control}
name="slug"
rules={{
required: t("common.errors.required"),
maxLength: {
value: 48,
message: t("workspace_creation.errors.validation.url_length"),
},
}}
render={({ field: { onChange, value, ref } }) => (
<Input
id="workspaceUrl"
type="text"
value={value.toLocaleLowerCase().trim().replace(/ /g, "-")}
onChange={(e) => {
const validation = validateSlug(e.target.value);
if (validation === true) setInvalidSlug(false);
else setInvalidSlug(true);
onChange(e.target.value.toLowerCase());
}}
ref={ref}
hasError={Boolean(errors.slug)}
placeholder={t("workspace_creation.form.url.placeholder")}
className="block w-full rounded-md border-none bg-transparent !px-0 py-2 text-12"
/>
)}
/>
</div>
<Controller
control={control}
name="slug"
rules={{
required: t("common.errors.required"),
maxLength: {
value: 48,
message: t("workspace_creation.errors.validation.url_length"),
},
}}
render={({ field: { onChange, value, ref } }) => (
<Field name="workspaceUrl" invalid={invalidSlug || Boolean(errors.slug)}>
<InputGroup size="2xl">
<span className="text-12 whitespace-nowrap text-secondary">{window && window.location.host}/</span>
<Input
size="2xl"
id="workspaceUrl"
type="text"
value={value.toLocaleLowerCase().trim().replace(/ /g, "-")}
onChange={(e) => {
const validation = validateSlug(e.target.value);
if (validation === true) setInvalidSlug(false);
else setInvalidSlug(true);
onChange(e.target.value.toLowerCase());
}}
ref={ref}
placeholder={t("workspace_creation.form.url.placeholder")}
/>
</InputGroup>
</Field>
)}
/>
{slugError && (
<p className="-mt-3 text-13 text-danger-primary">
{t("workspace_creation.errors.validation.url_already_taken")}

View File

@@ -8,11 +8,13 @@ import { observer } from "mobx-react";
import { Controller, useForm } from "react-hook-form";
import { AlertTriangle } from "lucide-react";
// Plane Imports
import { Field } from "@makeplane/propel/components/field";
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { useTranslation } from "@plane/i18n";
import { Button } from "@plane/propel/button";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import type { IWorkspace } from "@plane/types";
import { Input } from "@plane/ui";
import { cn } from "@plane/utils";
// hooks
import { useWorkspace } from "@/hooks/store/use-workspace";
@@ -106,18 +108,21 @@ export const DeleteWorkspaceForm = observer(function DeleteWorkspaceForm(props:
control={control}
name="workspaceName"
render={({ field: { value, onChange, ref } }) => (
<Input
id="workspaceName"
name="workspaceName"
type="text"
value={value}
onChange={onChange}
ref={ref}
hasError={Boolean(errors.workspaceName)}
placeholder={data?.name}
className="mt-2 w-full"
autoComplete="off"
/>
<Field name="workspaceName" invalid={Boolean(errors.workspaceName)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="workspaceName"
name="workspaceName"
type="text"
value={value}
onChange={onChange}
ref={ref}
placeholder={data?.name}
autoComplete="off"
/>
</InputGroup>
</Field>
)}
/>
</div>
@@ -132,18 +137,21 @@ export const DeleteWorkspaceForm = observer(function DeleteWorkspaceForm(props:
control={control}
name="confirmDelete"
render={({ field: { value, onChange, ref } }) => (
<Input
id="confirmDelete"
name="confirmDelete"
type="text"
value={value}
onChange={onChange}
ref={ref}
hasError={Boolean(errors.confirmDelete)}
placeholder=""
className="mt-2 w-full"
autoComplete="off"
/>
<Field name="confirmDelete" invalid={Boolean(errors.confirmDelete)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="confirmDelete"
name="confirmDelete"
type="text"
value={value}
onChange={onChange}
ref={ref}
placeholder=""
autoComplete="off"
/>
</InputGroup>
</Field>
)}
/>
</div>

View File

@@ -8,10 +8,12 @@ import { observer } from "mobx-react";
import type { Control, FieldArrayWithId, FormState } from "react-hook-form";
import { Controller } from "react-hook-form";
// plane imports
import { Field } from "@makeplane/propel/components/field";
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { ROLE } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import { CloseIcon } from "@plane/propel/icons";
import { CustomSelect, Input } from "@plane/ui";
import { CustomSelect } from "@plane/ui";
import { cn } from "@plane/utils";
// hooks
import { useUserPermissions } from "@/hooks/store/user";
@@ -62,17 +64,20 @@ export const InvitationFields = observer(function InvitationFields(props: TInvit
}}
render={({ field: { value, onChange, ref } }) => (
<>
<Input
id={`emails.${index}.email`}
name={`emails.${index}.email`}
type="text"
value={value}
onChange={onChange}
ref={ref}
hasError={Boolean(errors.emails?.[index]?.email)}
placeholder={t("workspace_settings.settings.members.modal.placeholder")}
className="w-full text-caption-sm-regular sm:text-body-xs-regular"
/>
<Field name="input" invalid={Boolean(errors.emails?.[index]?.email)}>
<InputGroup size="2xl">
<Input
size="2xl"
id={`emails.${index}.email`}
name={`emails.${index}.email`}
type="text"
value={value}
onChange={onChange}
ref={ref}
placeholder={t("workspace_settings.settings.members.modal.placeholder")}
/>
</InputGroup>
</Field>
{errors.emails?.[index]?.email && (
<span className="ml-1 text-caption-sm-regular text-danger-primary">
{errors.emails?.[index]?.email?.message}

View File

@@ -8,13 +8,15 @@ import { useEffect, useState } from "react";
import { observer } from "mobx-react";
import { Controller, useForm } from "react-hook-form";
// Plane Imports
import { Field } from "@makeplane/propel/components/field";
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { ORGANIZATION_SIZE, EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import { Button } from "@plane/propel/button";
import { EditIcon } from "@plane/propel/icons";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import type { IWorkspace } from "@plane/types";
import { CustomSelect, Input } from "@plane/ui";
import { CustomSelect } from "@plane/ui";
import { cn, copyUrlToClipboard, getFileURL, validateWorkspaceName } from "@plane/utils";
// components
import { WorkspaceImageUploadModal } from "@/components/core/modals/workspace-image-upload-modal";
@@ -198,18 +200,21 @@ export const WorkspaceDetails = observer(function WorkspaceDetails() {
validate: (value) => validateWorkspaceName(value, true),
}}
render={({ field: { value, onChange, ref } }) => (
<Input
id="name"
name="name"
type="text"
value={value}
onChange={onChange}
ref={ref}
hasError={Boolean(errors.name)}
placeholder={t("workspace_settings.settings.general.name")}
className="w-full rounded-md"
disabled={!isAdmin}
/>
<Field name="name" invalid={Boolean(errors.name)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="name"
name="name"
type="text"
value={value}
onChange={onChange}
ref={ref}
placeholder={t("workspace_settings.settings.general.name")}
disabled={!isAdmin}
/>
</InputGroup>
</Field>
)}
/>
{errors.name && <p className="text-caption-sm-regular text-danger-primary">{errors.name.message}</p>}
@@ -248,20 +253,23 @@ export const WorkspaceDetails = observer(function WorkspaceDetails() {
control={control}
name="url"
render={({ field: { onChange, ref } }) => (
<Input
id="url"
name="url"
type="url"
value={`${
typeof window !== "undefined" &&
window.location.origin.replace("http://", "").replace("https://", "")
}/${currentWorkspace.slug}`}
onChange={onChange}
ref={ref}
hasError={Boolean(errors.url)}
className="w-full cursor-not-allowed rounded-md !bg-layer-1"
disabled
/>
<Field name="url" invalid={Boolean(errors.url)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="url"
name="url"
type="url"
value={`${
typeof window !== "undefined" &&
window.location.origin.replace("http://", "").replace("https://", "")
}/${currentWorkspace.slug}`}
onChange={onChange}
ref={ref}
disabled
/>
</InputGroup>
</Field>
)}
/>
</div>

View File

@@ -9,13 +9,14 @@ import { observer } from "mobx-react";
import { useParams } from "next/navigation";
import type { SubmitHandler } from "react-hook-form";
import { Controller, useForm } from "react-hook-form";
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { useOutsideClickDetector } from "@plane/hooks";
import { useTranslation } from "@plane/i18n";
// plane helpers
// plane ui
import { FavoriteFolderIcon } from "@plane/propel/icons";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import { Input } from "@plane/ui";
// hooks
import { useFavorite } from "@/hooks/store/use-favorite";
@@ -146,12 +147,14 @@ export const NewFavoriteFolder = observer(function NewFavoriteFolder(props: TPro
control={control}
rules={{ required: true }}
render={({ field }) => (
<Input
className="w-full"
placeholder={t("new_folder")}
aria-label={t("aria_labels.projects_sidebar.enter_folder_name")}
{...field}
/>
<InputGroup size="2xl">
<Input
size="2xl"
placeholder={t("new_folder")}
aria-label={t("aria_labels.projects_sidebar.enter_folder_name")}
{...field}
/>
</InputGroup>
)}
/>
</form>

View File

@@ -7,12 +7,14 @@
import { observer } from "mobx-react";
import { Controller, useForm } from "react-hook-form";
// plane imports
import { Field } from "@makeplane/propel/components/field";
import { Input, InputGroup } from "@makeplane/propel/components/input";
import { ISSUE_DISPLAY_FILTERS_BY_PAGE } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import { Button } from "@plane/propel/button";
import type { IIssueDisplayFilterOptions, IIssueDisplayProperties, IWorkspaceView, IIssueFilters } from "@plane/types";
import { EViewAccess, EIssueLayoutTypes, EIssuesStoreType } from "@plane/types";
import { Input, TextArea } from "@plane/ui";
import { TextArea } from "@plane/ui";
import { getComputedDisplayFilters, getComputedDisplayProperties } from "@plane/utils";
// components
import { DisplayFiltersSelection, FiltersDropdown } from "@/components/issues/issue-layouts/filters";
@@ -90,17 +92,20 @@ export const WorkspaceViewForm = observer(function WorkspaceViewForm(props: Prop
},
}}
render={({ field: { value, onChange, ref } }) => (
<Input
id="name"
name="name"
type="name"
value={value}
onChange={onChange}
ref={ref}
hasError={Boolean(errors.name)}
placeholder={t("common.title")}
className="w-full text-14"
/>
<Field name="name" invalid={Boolean(errors.name)}>
<InputGroup size="2xl">
<Input
size="2xl"
id="name"
name="name"
type="name"
value={value}
onChange={onChange}
ref={ref}
placeholder={t("common.title")}
/>
</InputGroup>
</Field>
)}
/>
<span className="text-11 text-danger-primary">{errors?.name?.message}</span>