# Node.js -------------------------------------------------------------------------------- title: "はじめに" description: "Job Schedulingサービスを使用して誕生日グリーティングアプリケーションを構築し、指定した相手にメールで自動的に誕生日の挨拶を送信します。" last_updated: "2026-07-02T09:34:22.097Z" source: "https://docs.catalyst.zoho.com/ja/tutorials/birthday-greetings/nodejs/introduction/" service: "All Services" related: - プロジェクトディレクトリ構造 (/ja/cli/v1/project-directory-structure/introduction/) - Dynamic Cron (/ja/job-scheduling/help/cron/key-concepts/#cron-types) - Job Scheudling SDK (/ja/sdk/nodejs/v2/job-scheduling/overview/) - Job Scheduling API (/ja/api/code-reference/job-scheduling/jobpool/get-all-jobpool/#GetAllJobPools) -------------------------------------------------------------------------------- # Birthday Greetings App ### はじめに このチュートリアルでは、**Birthday Greetings**というシンプルなReact Webアプリケーションの構築方法を説明します。このアプリケーションでは、同僚の誕生日にメールで送信する挨拶を設定できます。カスタム誕生日メッセージを設定し、指定した相手のメールアドレスに誕生日当日に送信するようスケジュールできます。 このアプリケーションでは、以下の操作が可能です: * アプリケーションへの**サインアップ**/**ログイン**を行い、カスタムロジックによるアクセス検証を含む一般的な認証プロセスを利用できます。 * 設定した誕生日の挨拶を**作成**、**更新**、**削除**、および管理できます。 アプリケーションのクライアント側は以下のように表示されます: <br /> 以下のリンクからアプリケーションの機能を確認できます:アプリを試す! このチュートリアルのバックエンドロジックは**Node.js**ランタイムでコーディングされています。Birthday Greetingアプリケーションは、以下のCatalystサービスのコンポーネント機能を使用します: 1. Catalyst Serverless<br /> - Functions:バックエンドロジックは以下のファンクションタイプを使用してコーディングします: - Advanced I/O Function:クライアントとのやり取りおよびDynamic Cronの作成ロジックをこのファンクションにコーディングします。 - Basic I/O Function:カスタムユーザー検証のロジックをこのファンクションにコーディングします。 - Job Function:誕生日グリーティングを含むメールの送信をトリガーするロジックをこのファンクションにコーディングします。 2. Catalyst Cloud Scale<br /> - Data Store:グリーティングの作成およびスケジュール対象者の名前、カスタムメッセージ、誕生日、メールアドレスを保存します。 - ZCQL:クエリを使用してData Storeからデータの投稿および取得を行います。 - Authentication:Embedded Authenticationタイプを使用して、ユーザーがアプリケーションにサインアップまたはログインするための必要なログイン要素を実装します。 - Whitelisting:Custom User Validation機能を使用してカスタム認証ロジックを実装します。 - Email Templates:**Email Verification**および**Forgot Password**のメールテンプレートをカスタマイズします。 - Mail:送信者のメールアドレスを確認し、指定した相手にメールを送信します。 - Web Client Hosting:アプリケーションのフロントエンドをホストします。クライアントを*React app*として初期化および設定できます。 3. Catalyst Job Scheduling<br /> - Job Pool:誕生日グリーティングを含むメールの送信をトリガーするために必要なJob Functionを実行するファンクションジョブを実行します。 - Cron:Dynamic Cronを使用してグリーティングメールの送信をスケジュールします。 - Jobs:Job Poolから実行される際に必要なJob Functionをトリガーします。 このアプリケーションの構築には、Catalyst WebコンソールとCatalyst Command Line Interface(CLI)を使用します。 Note: このチュートリアルでは、ファンクションおよびクライアントコンポーネントに含めるファイルのコードが提供されます。提供されたコードをコピーし、指示に従って適切なファイルに貼り付けるだけで構いません。 ### アプリケーションのワークフロー Birthday Greetingsアプリケーションのワークフローを以下に示します: <br /> 1. アプリケーションにサインアップまたはログインします。 2. クライアント側のUI要素を使用して、カスタムメッセージの入力、誕生日グリーティングの送信日時のスケジュール、送信先の指定を行います。 3. これらの詳細は*Advanced I/O function*によって使用され、SDKを使用して*Dynamic Cron*が作成されます。 4. スケジュールされた時刻に、Dynamic Cronがファンクションジョブプールに*ファンクションジョブ*を送信し、そこからファンクションジョブが実行されて*Job Function*がトリガーされます。 5. Job FunctionはMail SDKを使用して、指定した相手に誕生日グリーティングを含むメールを送信します。 アプリケーションの全体的なワークフローは、クライアントロジックをコーディングする際により詳しく説明されます。手順に沿って進める際に確認することをお勧めします。 -------------------------------------------------------------------------------- title: "前提条件" description: "Job Schedulingサービスを使用して誕生日グリーティングアプリケーションを構築し、指定した相手にメールで自動的に誕生日の挨拶を送信します。" last_updated: "2026-07-02T09:34:22.097Z" source: "https://docs.catalyst.zoho.com/ja/tutorials/birthday-greetings/nodejs/prerequisites/" service: "All Services" related: - Catalyst CLIドキュメント (/ja/cli/v1/cli-command-reference/) - Catalyst VS Code Extension (/ja/catalyst-extensions/vs-code-extension/introduction/) -------------------------------------------------------------------------------- # 前提条件 アプリケーションの構築を開始する前に、以下の前提条件がシステムにインストールされている必要があります: 1. Catalyst CLI:Catalyst CLIには、ローカルマシンからアプリケーションのコンポーネントを初期化、開発、テスト、およびデプロイするためのツール一式が含まれています。このチュートリアルではCatalyst CLIを使用します。以下の操作を行う必要があります: - **Catalyst CLIのインストール**:Catalyst CLIはNPMを通じてインストールされます。CLIをインストールする前に、NPMとNode.jsがシステムにインストールされている必要があります。前提条件とインストール手順の詳細については、Catalyst CLIインストールヘルプページを参照してください。 - **Catalyst CLIへのログイン**:Catalyst CLIをインストールした後、使用する前にCatalystアカウントでCLIを認証する必要があります。Catalyst CLIからのログイン手順と利用可能なオプションについては、CLIログインヘルプページを参照してください。 2. Node.jsおよびクライアントコード開発用のIDEツール:任意のIDEを使用してファンクションおよびクライアントコードを操作できます。一般的な選択肢には、Visual Studio Code、IntelliJ IDEA、Eclipse、Sublime Textなどがあります。お好みのIDEをダウンロードしてシステムにインストールしてください。 Visual Studio Code IDEをご利用の場合は、Catalyst Tools拡張機能をインストールすることで、CLIの代わりにIDE自体を使用できます。Catalyst VS Code拡張機能の詳細については、このヘルプセクションをご覧ください。 -------------------------------------------------------------------------------- title: "プロジェクトの作成" description: "Job Schedulingサービスを使用して誕生日グリーティングアプリケーションを構築し、指定した相手にメールで自動的に誕生日の挨拶を送信します。" last_updated: "2026-07-02T09:34:22.097Z" source: "https://docs.catalyst.zoho.com/ja/tutorials/birthday-greetings/nodejs/create-project/" service: "All Services" related: - Catalystプロジェクト (/ja/getting-started/catalyst-projects/) -------------------------------------------------------------------------------- # プロジェクトの作成 CatalystコンソールからCatalystプロジェクトを作成しましょう。 1. Catalystコンソールにログインし、**Create New Project**をクリックします。 <br /> 2. ポップアップウィンドウでプロジェクト名を「**BirthdayGreetings**」と入力します。 <br /> プロジェクトが作成され、開きます。 <!-- You can open the project by clicking **Access Project**. <br /> --> -------------------------------------------------------------------------------- title: "テーブルの作成" description: "Job Schedulingサービスを使用して誕生日グリーティングアプリケーションを構築し、指定した相手にメールで自動的に誕生日の挨拶を送信します。" last_updated: "2026-07-02T09:34:22.097Z" source: "https://docs.catalyst.zoho.com/ja/tutorials/birthday-greetings/nodejs/create-table/" service: "All Services" related: - Data Store (/ja/cloud-scale/help/data-store/introduction/) - スコープと権限 (/ja/cloud-scale/help/data-store/scopes-and-permissions/) - ロール (/ja/cloud-scale/help/authentication/user-management/roles/introduction/) -------------------------------------------------------------------------------- # テーブルの作成 次に、Data Storeにテーブルを作成します。このテーブルは、必要な詳細を保存するために以下のカラムを作成するのに使用されます: <table class="content-table"> <thead> <tr> <th class="w25p">カラム名</th> <th class="w25p">データ型</th> <th class="w50p">目的</th> </tr> </thead> <tbody> <tr> <td>Name</td> <td>Var Char</td> <td>挨拶を送る相手の名前を保存します。</td> </tr> <tr> <td>Message</td> <td>Text</td> <td>カスタムメッセージを保存します。</td> </tr> <tr> <td>BirthDay</td> <td>Date</td> <td>誕生日の日付を保存します。</td> </tr> <tr> <td>Email</td> <td>Var Char</td> <td>受信者のメールアドレスを保存します。</td> </tr> <tr> <td>AutoSend</td> <td>Boolean</td> <td>スケジュールされた時刻に指定した相手にメールを自動送信するかどうかを選択します。デフォルト値はTrueです。</td> </tr> </tbody> </table> テーブルを作成するには: 1. コンソールのCatalyst *Cloud Scale*サービスセクションに移動し、**Start Exploring**をクリックします。 <br /> 2. *STORAGE*セクション内の*Data Store*コンポーネントに移動し、**Create a new Table**をクリックします。 <br /> 3. テーブル名を「BirthDayReminder」と入力し、**Create**をクリックします。 <br /> Note: アプリケーションのコードに同じ名前が含まれているため、指示どおりに正確に名前を入力してください。 テーブルが作成されます。 <br /> 次に、必要なカラムを作成します。 Info: Catalystがサポートするさまざまなデータ型やカラムのその他のプロパティについては、Data Storeヘルプドキュメントをご覧ください。 4. *Schema View*セクションの**New Column**をクリックします。 <br /> 5. カラム名を「Name」と入力します。データ型として**Var Char**を選択し、**Max Length**に「100」を入力します。 <br /> 6. **Create**をクリックします。 <br /> 7. **New Column**ボタンを再度クリックして2番目のカラムを作成します。名前を「Message」とし、データ型として**Text**を選択して、**Create**をクリックします。 <br /> 8. **New Column**ボタンを再度クリックして3番目のカラムを作成します。名前を「BirthDay」とし、データ型として**Date**を選択して、**Create**をクリックします。 <br /> 9. **New Column**ボタンを再度クリックして4番目のカラムを作成します。名前を「Email」とし、データ型として**Var Char**を選択し、**Max Length**に「120」を入力して、**Create**をクリックします。 <br /> 10. 最後に、**New Column**ボタンを再度クリックして5番目のカラムを作成します。名前を「AutoSend」とし、データ型として**Boolean**を選択し、**Default Value**として**True**を選択して、**Create**をクリックします。 <br /> 必要なカラムがすべて作成されました。 <br /> ### スコープと権限の設定 Birthday Greetingsアプリケーションのすべてのユーザーがクライアントアプリケーションから誕生日メッセージを作成および管理できるようにするには、*App User*プロファイルのCRUD操作を有効にし、そのロールを*User*に設定する必要があります。 必要な権限を有効にするには: 1. **Scopes & Permissions**タブをクリックします。 <br /> 2. *App User*ロールに**User**プロファイルを選択します。 <br /> 3. 各チェックボックスをクリックして、*App User*の**Update**、**Insert**、**Delete**権限を選択します。その他の権限設定は変更しないでください。 <br /> *Data Store*コンポーネントがアプリケーション用に設定されました。 -------------------------------------------------------------------------------- title: "送信者メールアドレスの設定" description: "Job Schedulingサービスを使用して誕生日グリーティングアプリケーションを構築し、指定した相手にメールで自動的に誕生日の挨拶を送信します。" last_updated: "2026-07-02T09:34:22.098Z" source: "https://docs.catalyst.zoho.com/ja/tutorials/birthday-greetings/nodejs/setup-mail/" service: "All Services" related: - Mail (/ja/cloud-scale/help/mail/introduction/) - ドメイン (/ja/cloud-scale/help/mail/domains/) - メール設定 (/ja/cloud-scale/help/mail/email-configuration/) -------------------------------------------------------------------------------- # 送信者メールアドレスの設定 *Catalyst Cloud Scale*コンポーネントのMailを使用して、送信者メールアドレスを設定します。このアドレスから、指定した相手に誕生日の挨拶が送信されます。 送信者メールアドレスを設定するには: 1. *Cloud Scale*コンソールの*NOTIFY*セクション内の**Mail**コンポーネントに移動し、**Add Email**をクリックします。 <br /> 2. 送信者メールアドレスの必要な情報を入力し、**Add Email**をクリックします。ご自身が使用しているメールアドレスを指定できます。 <br /> 3. メールアドレスが*Email Configuration*セクションに表示されます。 <br /> Note: このメールアドレスが、誕生日の挨拶の送信元メールIDとなります。 4. **Click to confirm**をクリックして、提供されたメールアドレスを確認します。 <br /> この操作により、Catalystが生成した確認コードが、指定したメールアドレスに自動的に送信されます。 <br /> 5. メールから確認コードをコピーし、指定された欄に貼り付けて**Confirm**をクリックします。メールIDが確認され、*Confirmation Status*列にVerifiedステータスが表示されます。 <br /> 送信者メールアドレスの設定が完了しました。 ### ドメインの設定 次に、アプリケーションからメールを送信するために使用するドメインを確認する必要があります。この確認により、メールがスパムフォルダではなく、目的の受信トレイに安全に届くようになります。 Note: これは、CatalystアプリケーションからのメールがDKIMプロトコルに厳密に準拠して送信されるようにするための必須の手順です。 ドメインを確認するには: 1. *Domain*タブに移動し、**Add Domain**をクリックします。 <br /> 2. 設定する送信者メールアドレスを入力し、**Create**をクリックします。 <br /> 3. ドメインが*Domain*タブに表示されます。**Yet to verify**オプションの隣にある**Verify code**ボタンをクリックします。 <br /> この操作により、Catalystが生成した確認コードがメールで自動的に送信されます。 <br /> 4. メールから確認コードをコピーし、指定された欄に貼り付けて**Confirm**をクリックします。 <br /> ドメインが確認され、Email Verifiedバッジがドメインに追加されます。 <br /> Info: DomainセクションおよびMailコンポーネントの詳細については、このヘルプドキュメントをご覧ください。 Mailコンポーネントがアプリケーション用に設定されました。 -------------------------------------------------------------------------------- title: "認証の有効化" description: "Job Schedulingサービスを使用して誕生日グリーティングアプリケーションを構築し、指定した相手にメールで自動的に誕生日の挨拶を送信します。" last_updated: "2026-07-02T09:34:22.098Z" source: "https://docs.catalyst.zoho.com/ja/tutorials/birthday-greetings/nodejs/enable-auth/" service: "All Services" related: - 認証 (/ja/cloud-scale/help/authentication/introduction/) - 埋め込み認証 (/ja/cloud-scale/help/authentication/native-catalyst-authentication/embedded-authentication/introduction/) - パブリックサインアップ (/ja/cloud-scale/help/authentication/public-signup/) - ソーシャルログイン (/ja/cloud-scale/help/authentication/social-logins/introduction/) - ホワイトリスト (/ja/cloud-scale/help/authentication/whitelisting/introduction/) - メールテンプレート (/ja/cloud-scale/help/authentication/email-templates/introduction/) -------------------------------------------------------------------------------- # 認証の有効化 このチュートリアルでは、アプリケーションにエンドユーザーを追加するためにEmbedded Authenticationタイプを有効にします。 アプリケーションでEmbedded Authenticationを有効にするには: 1. *SECURITY & IDENTITY*内の*Authentication*コンポーネントに移動し、**Native Authentication**オプションの**Set Up**をクリックします。 <br /> 2. **Embedded Authentication**を選択し、**Next**をクリックします。 <br /> 3. このセクションに記載されているスクリプトは、クライアントを設定する際に実装します。ここでは、**Public Signup**のトグルをクリックして、ユーザーがアプリケーションにサインアップできるようにします。 <br /> Info: **Public Signup**機能の詳細については、このヘルプドキュメントをご覧ください。 4. ポップアップの情報を確認し、**Yes, Proceed**をクリックします。 <br /> 5. *Public Signup*が有効になりました。エンドユーザーがアプリケーションにサインアップでき、必要に応じてSocial Loginsを使用したサインアップも許可できます。**Next**をクリックします。 <br /> Info: このチュートリアルではSocial Logins機能は取り扱いません。詳細については、このヘルプドキュメントをご覧ください。 6. *Additional Settings*セクションは現時点ではスキップします。このセクションにある*Custom User Validation*機能は、後の手順で再度設定します。**Finish**をクリックします。 <br /> アプリケーションでEmbedded Authenticationが有効になりました。 <br /> ### メールテンプレートの設定 **Email Templates**は*Authentication*の機能で、エンドユーザーが登録またはパスワードリセットを行う際に送信される*Email Verification*および*Password Reset*メールのスタイルやコンテンツをカスタマイズできます。 必要なメールテンプレートを設定するには: 1. *Authentication*コンポーネントの**Email Templates**タブをクリックして該当セクションに移動します。**Edit**をクリックして**Email Verification**テンプレートをカスタマイズします。 <br /> 2. コンテンツをカスタマイズおよび編集します。メールが呼び出される際に動的に値が挿入されるプレースホルダーをテンプレートに含めることもできます。必要な変更を行い、**Save**をクリックします。 <br /> Notes:<br /> * メールのコンテンツはお好みに合わせてカスタマイズできます。 * プレースホルダーコンテンツ(%%で囲まれた内容)は、ユーザーの詳細に基づいて動的に変更されレンダリングされます。 * %LINK%プレースホルダーには、Catalystが自動生成するアプリケーションのリンクが含まれます。カスタムリンクを指定することも可能です。 3. Password Resetメールテンプレートをカスタマイズするには、**Password Reset**タブをクリックし、上記と同様にメールのコンテンツをカスタマイズします。 <br /> メールテンプレートの設定が完了しました。 Info:<br /> * Email Templatesの詳細については、このヘルプドキュメントをご覧ください。 * このアプリケーションの認証にEmbedded Authentication(**Native Authentication**タイプ)を使用しているため、必要なタイミングでこれらのメールを送信する処理はCatalystによって自動的に設定されます。そのため、メール送信をトリガーするロジックをコーディングする必要はありません。 -------------------------------------------------------------------------------- title: "プロジェクトの初期化" description: "Job Schedulingサービスを使用して誕生日グリーティングアプリケーションを構築し、指定した相手にメールで自動的に誕生日の挨拶を送信します。" last_updated: "2026-07-02T09:34:22.098Z" source: "https://docs.catalyst.zoho.com/ja/tutorials/birthday-greetings/nodejs/init-project/" service: "All Services" related: - Catalyst CLI (/ja/cli/v1/cli-command-reference/) - リソースの初期化 (/ja/cli/v1/initialize-resources/introduction/) - プロジェクトディレクトリ (/ja/cli/v1/project-directory-structure/introduction/) - Catalyst Functions (/ja/serverless/help/functions/introduction/) -------------------------------------------------------------------------------- # プロジェクトの初期化 これで、CLIからCatalystプロジェクトの作業を開始できます。最初のステップは、空のディレクトリでプロジェクトを初期化することです。これがプロジェクトのホームディレクトリとなり、すべてのプロジェクトファイルがここに保存されます。 詳細については、プロジェクトディレクトリ構造ヘルプページをご覧ください。プロジェクトの初期化の詳細については、CLIヘルプドキュメントを参照してください。 このアプリケーションでは、Custom User Validationのロジックを含むBasic I/O functionと、クライアントコンポーネントとしてReact web appを初期化します。後ほど、Advanced I/O functionとJob Functionも初期化します。 Note: アプリケーションのコードに同じ名前が含まれているため、ファンクションのパッケージ名またはクラス名は指示どおりに正確に入力してください。 1. ローカルマシンにプロジェクト用のフォルダを作成し、ターミナルからそのフォルダに移動します。 2. そのディレクトリから以下のコマンドを実行してプロジェクトを初期化します: catalyst init 3. **矢印キー**で移動し、お好みのポータルを選択して**Enter**キーを押します。アカウントに他の組織が関連付けられていない場合は、デフォルトのものが自動的に選択されます。 <br /> Info: Catalystのマルチ組織ポータル機能の詳細については、このヘルプドキュメントをご覧ください。 4. CLIがCatalystプロジェクトとディレクトリの関連付けを求めます。先ほどコンソールから作成したプロジェクトに関連付けます。リストから**BirthdayGreetings**を選択し、**Enter**をクリックします。 <br /> 5. **スペースバー**で**Functions**と**Client**を選択し、**Enter**をクリックして初期化します。 <br /> 6. CLIがファンクションのセットアップを開始します。*Custom User Validation*ファンクションをコーディングするために、ファンクションタイプとして**Basic IO**を選択します。 <br /> 7. ファンクションスタックとして最新ランタイムの**Node.js**を選択します。 <br /> 8. パッケージ名に「basic_function」、エントリーポイントに「index.js」、作成者にメールアドレスを入力し、**Enter**を押します。または、入力せずに**Enter**を押すとデフォルト値が設定されます。CLIがNode依存関係の初期化を求めます。**Y**を押してインストールを確認し、**Enter**を押して選択を確定します。Nodeモジュールがインストールされます。 <br /> CLIがクライアントのセットアップを開始します。 9. **React web app**を選択し、**Enter**を押してクライアントをReact web appとして初期化します。 <br /> 10. Reactアプリタイプとして**JavaScript**を選択し、**Enter**を押します。 <br /> 11. クライアントパッケージ名として「birthdaygreetings」を入力し、**Enter**をクリックします。任意の名前を指定することも可能です。プロンプトで「**y**」を入力して、「react」、「react-dom」、「react-scripts」などの必要なReactパッケージをインストールします。これらのパッケージはCatalyst Reactプラグイン(zcatalyst-cli-plugin-react)を通じてインストールされます。 <br /> クライアントがReact Webアプリケーションとして正常に初期化されました。 <br /> プロジェクトディレクトリに標準構造でクライアントディレクトリが作成されます。 以下は、*Birthday Greetings*プロジェクトディレクトリの現在の構造です。 <br /> 次に、BirthdayGreetings/functions/ディレクトリで以下のCLIコマンドを使用して、Advanced I/O FunctionとJob Functionの初期化を行います: catalyst functions:add このCLIコマンドはファンクションのセットアップをトリガーし、プロジェクトディレクトリに必要なファンクションを追加できます。 1. ファンクションセットアップが開始されたら、**Advanced IO**ファンクションを選択します。 <br /> 2. ファンクションスタックとして最新ランタイムの**Node.js**を選択します。 <br /> 3. パッケージ名に「advance_function」、エントリーポイントに「index.js」、作成者にメールアドレスを入力し、**Enter**を押します。または、入力せずに**Enter**を押すとデフォルト値が設定されます。CLIがNode依存関係の初期化を求めます。**Y**を押してインストールを確認し、**Enter**を押して選択を確定します。Nodeモジュールがインストールされます。 <br /> Advanced I/O functionが初期化されました。 同じCLIコマンドを使用してJob Functionを初期化しましょう。 catalyst functions:add 1. ファンクションセットアップが開始されたら、ファンクションタイプとして**Job**を選択します。 <br /> 2. ファンクションスタックとして最新ランタイムの**Node.js**を選択します。 <br /> 3. パッケージ名に「dynamic_cron」、エントリーポイントに「index.js」、作成者にメールアドレスを入力し、**Enter**を押します。または、入力せずに**Enter**を押すとデフォルト値が設定されます。CLIがNode依存関係の初期化を求めます。**Y**を押してインストールを確認し、**Enter**を押して選択を確定します。Nodeモジュールがインストールされます。 <br /> アプリケーションに必要なすべてのクライアントおよびファンクションコンポーネントが初期化されました。以下はプロジェクトディレクトリの現在の構造です: <br /> -------------------------------------------------------------------------------- title: "Custom User Validationの有効化" description: "Job Schedulingサービスを使用して誕生日グリーティングアプリケーションを構築し、指定した相手にメールで自動的に誕生日の挨拶を送信します。" last_updated: "2026-07-02T09:34:22.098Z" source: "https://docs.catalyst.zoho.com/ja/tutorials/birthday-greetings/nodejs/enable-custom-logic/" service: "All Services" related: - 認証 (/ja/cloud-scale/help/authentication/introduction/) - 認証SDK (/ja/sdk/nodejs/v2/cloud-scale/authentication/get-component-instance/) - ホワイトリスト (/ja/cloud-scale/help/authentication/whitelisting/introduction/) - カスタムユーザー検証 (/ja/cloud-scale/help/authentication/whitelisting/custom-user-validation/introduction/) -------------------------------------------------------------------------------- # Custom User Validationの有効化 ### カスタム認証ロジックのコーディング ここでは、エンドユーザーがアプリケーションに登録する際に適用するカスタムロジックをコーディングします。 ファンクションディレクトリ BirthdayGreetings/functions/basic_function/には以下が含まれます: * index.jsメインファンクションファイル * catalyst-config.json設定ファイル * Nodeモジュール * package.jsonおよびpackage-lock.json依存関係ファイル。 index.jsファイルにコードを追加します。 このファンクションには以下の機能が含まれます: * エンドユーザーの詳細がJSON入力としてCustom User Validationファンクションに渡されます。 * このファンクションは、エンドユーザーの認証または拒否を行うカスタムロジックを実装するようコーディングされています。 Note: このチュートリアルでは、Custom User Validationファンクションの例として、ユーザーのメールプロバイダーがZylker Technology(@zylker.com)以外の場合、エンドユーザーはアプリケーションにサインアップできないというロジックをコーディングしています。同じロジックを使用するか、お好みのロジックをコーディングしてください。 以下のコードをコピーして、プロジェクトのBirthdayGreetings/functions/basic_functionディレクトリにあるindex.jsファイルに貼り付け、ファイルを保存してください。 アプリケーションのファイル操作には、任意のIDEを使用できます。 Note: このセクションのコードを十分に理解するために、必ず目を通してください。 const catalyst = require('zcatalyst-sdk-node') module.exports = (context, basicIO) => { const catalystApp = catalyst.initialize(context) const requestDetails = catalystApp.userManagement().getSignupValidationRequest(basicIO) if (requestDetails) { if (requestDetails.user_details.email_id.includes('@zylker.com')) { basicIO.write(JSON.stringify({ status: 'success', user_details: { first_name: requestDetails.user_details.first_name, last_name: requestDetails.user_details.last_name, email_id: requestDetails.user_details.email_id, role_identifier: 'App User', org_id: '' } })) } else { // ユーザーの認証に失敗しました basicIO.write(JSON.stringify({ status: 'failure' })) } } context.close() } Notes: * 6行目のドメイン名を、要件に合ったものに変更してください。 * 上記の実装の代わりに、このSDKを使用して独自のカスタムロジックをコーディングすることもできます。 ### ファンクションのデプロイとCustom User Validationの設定 このカスタムロジックを適用するには、コンソールでCustom User Validationオプションを有効にするために、このファンクションをコンソールにデプロイする必要があります。 ファンクションをデプロイするには、以下のCLIコマンドを実行してファンクションコンポーネントのみをコンソールにデプロイします。 catalyst deploy --only functions Info: 代わりに、以下のコマンドを使用してCustom User Validationファンクションのみをデプロイすることもできます:<br /> catalyst deploy --only functions:function_name <br /> すべてのファンクションに対してファンクションエンドポイントが作成されます。これにより、ファンクションに直接アクセスできるようになります。 ファンクションがコンソールにデプロイされます。コンソールのServerlessセクション内のFunctionsコンポーネントからアクセスできます。 <br /> Note: プロジェクトのすべてのファンクションをコンソールにデプロイしましたが、**Advanced I/O**および**Job functions**のコーディング後に再度デプロイを行います。 次に、コンソールの*Cloud Scale*セクション内の**Authentication**コンポーネントに移動し、以下の手順を実行します: 1. *Authentication*コンポーネントの**Whitelisting**セクションに移動し、**Custom User Validation**のトグルをクリックします。 <br /> 2. ポップアップのドロップダウンから**Basic I/O**ファンクションを選択します。 <br /> 3. **Configure**をクリックします。 <br /> Custom User Validationファンクションがプロジェクトで有効になり、アプリケーションにサインアップするエンドユーザーはこのファンクションを使用して追加の認証が行われるようになります。 Note: Whitelistingセクションに存在するすべての機能については、このヘルプドキュメントをご覧ください。 -------------------------------------------------------------------------------- title: "Job Schedulingサービスの設定" description: "Job Schedulingサービスを使用して誕生日グリーティングアプリケーションを構築し、指定した相手にメールで自動的に誕生日の挨拶を送信します。" last_updated: "2026-07-02T09:34:22.098Z" source: "https://docs.catalyst.zoho.com/ja/tutorials/birthday-greetings/nodejs/config-jobscheduling/" service: "All Services" related: - Job Scheduling Service (/ja/job-scheduling/) - Dynamic Cron (/ja/job-scheduling/help/cron/key-concepts/#cron-types) - Job Pool (/ja/job-scheduling/help/jobpool/introduction/) - Job Scheduling SDK (/ja/sdk/nodejs/v2/job-scheduling/overview/) - Job Scheduling APIs (/ja/api/code-reference/job-scheduling/jobpool/get-all-jobpool/#GetAllJobPools) -------------------------------------------------------------------------------- # Job Schedulingサービスの設定 ここでは、必要な誕生日グリーティングが正確なタイミングで送信されるように、Job Schedulingサービスのコンポーネントを設定します。 まず、ジョブを実行するための**Job Pool**を作成します。 Job Poolを作成するには: 1. コンソールの*Job Scheduling*セクションに移動し、**Start Exploring**をクリックします。 <br /> 2. **Job Pool**コンポーネントに移動し、**Create Job Pool**をクリックします。 <br /> 3. Job Pool名を「MessageTrigger」と入力し、ドロップダウンからJob Poolタイプとして**Function**を選択します。 <br /> 4. ドロップダウンから**Max Allocated Memory**として2GBを選択し、**Create**をクリックします。 <br /> Job Poolが作成されました。 <br /> Job Poolは名前およびIDで参照できます。 Job Poolは、必要なJob Functionをトリガーするファンクションジョブを実行する準備が整いました。 -------------------------------------------------------------------------------- title: "Advanced I/O Functionのコーディング" description: "Job Schedulingサービスを使用して誕生日グリーティングアプリケーションを構築し、指定した相手にメールで自動的に誕生日の挨拶を送信します。" last_updated: "2026-07-02T09:34:22.098Z" source: "https://docs.catalyst.zoho.com/ja/tutorials/birthday-greetings/nodejs/code-advancedio/" service: "All Services" related: - Advanced I/O Functions (/ja/serverless/help/functions/advanced-io/) - Node.js SDK (/ja/sdk/nodejs/v2/overview/) -------------------------------------------------------------------------------- # Advanced I/O Functionのコーディング Advanced I/O functionの設定を始めましょう。このファンクションはクライアントとの通信に使用され、Dynamic Cronを設定します。Cronのメタデータは、Cron作成SDKを使用してクライアントで入力されたデータから取得されます。 ファンクションディレクトリ内のAdvanced I/O function BirthdayGreetings/functions/advance_function/には以下が含まれます: * index.jsメインファンクションファイル * catalyst-config.json設定ファイル * Nodeモジュール * package.jsonおよびpackage-lock.json依存関係ファイル。 index.jsファイルにコードを追加します。 このファンクションをコーディングするには、Express Node.jsフレームワークを使用します。コード内でExpressパッケージをインポートするには、システムにExpressの依存関係をインストールする必要があります。 ローカルマシンにExpress.jsをインストールするには、ターミナルでBirthdayGreetings/functions/advance_function/に移動し、以下のコマンドを実行します: npm install express --save <br /> 以下のコードをコピーしてindex.jsファイルに貼り付けてください。 Note: このセクションのコードを十分に理解するために、必ず目を通してください。 'use strict'; const catalyst = require('zcatalyst-sdk-node'); const express = require('express'); const app = express(); app.use(express.json()); app.post('/insertReminder', async (req, res) => { console.log("Request body:", req.body); try { const catalystApp = catalyst.initialize(req); let userManagement = catalystApp.userManagement(); let userPromise = userManagement.getCurrentUser(); userPromise.then(async (currentUser) => { console.log(currentUser); const datastores = catalystApp.datastore(); const table = datastores.table('12096000001771061'); // テーブルID await insertReminder(req.body, table, catalystApp); res.status(200).json({ status: 200, message: "Reminder added successfully" }); }); } catch (error) { console.error('Error adding reminder:', error); res.status(500).json({ Error: error.message }); } }); app.get('/getReminder', async (req, res) => { try { const catalystApp = catalyst.initialize(req); const datastores = catalystApp.datastore(); const table = datastores.table('12096000001771061'); // テーブルID const reminders = await getReminders(catalystApp, table); res.status(200).json(reminders); } catch (error) { console.error('Error retrieving reminders:', error); res.status(500).json({ Error: error.message }); } }); app.put('/updateReminder', async (req, res) => { try { const catalystApp = catalyst.initialize(req); const datastores = catalystApp.datastore(); const table = datastores.table('12096000001771061'); // テーブルID await updateReminder(req.body, table, catalystApp); res.status(200).json({ status: 200, message: "Reminder updated successfully" }); } catch (error) { console.error('Error updating reminder:', error); res.status(500).json({ Error: error.message }); } }); app.patch('/toggleAutoSend', async (req, res) => { try { const catalystApp = catalyst.initialize(req); const datastores = catalystApp.datastore(); const table = datastores.table('12096000001771061'); // テーブルID await toggleAutoSend(req.body, table, catalystApp); res.status(200).json({ status: 200, message: "Auto send toggled status successfully" }); } catch (error) { console.error('Error toggling status:', error); res.status(500).json({ Error: error.message }); } }); app.delete('/deleteReminder/:id', async (req, res) => { try { const { id } = req.params; console.log("ID received:", id); const catalystApp = catalyst.initialize(req); const datastores = catalystApp.datastore(); const table = datastores.table('12096000001771061'); // テーブルID await deleteReminder(id, table, catalystApp); res.status(200).json({ status: 200, message: "Reminder deleted successfully" }); } catch (error) { console.error('Error deleting reminder:', error); res.status(500).json({ Error: error.message }); } }); module.exports = app; async function toggleAutoSend(requestBody, table, catalystApp) { const { id, status } = requestBody; const enableStatus = status === 'enable'; const zcql = catalystApp.zcql(); const query = `SELECT * FROM BirthDayReminder WHERE ROWID = '${id}'`; const response = await zcql.executeZCQLQuery(query); if (!response || response.length === 0) throw new Error('Reminder not found.'); const row = response[0].BirthDayReminder; row.AutoSend = enableStatus; console.log("Row.autosend ", row.AutoSend); const jobScheduling = catalystApp.jobScheduling(); if (!enableStatus) { console.log("false"); await jobScheduling.CRON.deleteCron(`bday_${id.substring(10, 17)}`); } else { console.log("true"); await scheduleCronJob(row, catalystApp); } const updateQuery = `UPDATE BirthDayReminder SET AutoSend = ${enableStatus} WHERE ROWID = '${id}'`; await zcql.executeZCQLQuery(updateQuery); } async function insertReminder(requestBody, table, catalystApp) { const { name, birthday, message, email } = requestBody; if (!name || !birthday || !message || !email) throw new Error('Missing required fields.'); const row = { Name: name, BirthDay: birthday, Message: message, Email: email }; try { const insertedRow = await table.insertRow(row); await scheduleCronJob(insertedRow, catalystApp); } catch (error) { console.error('Error inserting reminder:', error); throw error; } } async function getReminders(catalystApp, table) { try { const zcql = catalystApp.zcql(); const query = 'SELECT * FROM BirthDayReminder'; const response = await zcql.executeZCQLQuery(query); if (!Array.isArray(response)) throw new Error('Invalid response from ZCQL query'); return response.map(row => { const reminder = row.BirthDayReminder; return { ID: reminder.ROWID || 'N/A', Name: reminder.Name || 'N/A', BirthDay: reminder.BirthDay || 'N/A', Message: reminder.Message || 'N/A', Email: reminder.Email || 'N/A', AutoSend: reminder.AutoSend || false }; }); } catch (error) { console.error('Error retrieving reminders:', error.message); throw error; } } async function updateReminder(requestBody, table, catalystApp) { const { ID, Name, BirthDay, Message, Email } = requestBody; const jobScheduling = catalystApp.jobScheduling(); if (!ID || !Name || !BirthDay || !Message || !Email) throw new Error('Missing required fields.'); const zcql = catalystApp.zcql(); const query = `SELECT AutoSend FROM BirthDayReminder WHERE ROWID = '${ID}'`; const response = await zcql.executeZCQLQuery(query); const AutoSend = response[0]?.BirthDayReminder?.AutoSend || false; const row = { ROWID: ID, Name: Name, BirthDay: BirthDay, Message: Message, Email: Email }; if (AutoSend) { await jobScheduling.CRON.deleteCron(`bday_${ID.substring(10, 17)}`); try { await table.updateRow(row); await scheduleCronJob(row, catalystApp); } catch (error) { console.error('Error updating reminder:', error); throw error; } } else { try { await table.updateRow(row); console.log('Row updated:', row); } catch (error) { console.error('Error updating reminder:', error); throw error; } } } async function deleteReminder(id, table, catalystApp) { if (!id) throw new Error('ID is missing.'); const zcql = catalystApp.zcql(); const query = `SELECT AutoSend FROM BirthDayReminder WHERE ROWID = '${id}'`; const response = await zcql.executeZCQLQuery(query); const AutoSend = response[0]?.BirthDayReminder?.AutoSend || false; const jobScheduling = catalystApp.jobScheduling(); if (AutoSend) { await jobScheduling.CRON.deleteCron(`bday_${id.substring(10, 17)}`); } await table.deleteRow(id); console.log('Reminder deleted:', id); } async function scheduleCronJob(row, catalystApp) { const jobScheduling = catalystApp.jobScheduling(); const { ROWID, BirthDay, Name, Email, Message } = row; const dob = new Date(BirthDay); const cronDetail = { cron_name: `bday_${ROWID.substring(10, 17)}`, description: `Birthday reminder for ${Name}`, cron_status: true, cron_type: 'CALENDAR', cron_detail: { hour: 0, minute: 0, second: 0, days: [dob.getDate()], months: [dob.getMonth()], repetition_type: 'YEARLY' }, job_meta: { job_name: `bday_${ROWID.substring(10, 17)}`, jobpool_name: 'Trigger_Message', // ジョブプール名 jobpool_id: '12096000001771827', // ジョブプールID target_type: 'FUNCTION', target_name: 'dynamic_cron', // ジョブファンクション名 job_service_identifier: 'default', retries: 2, retry_interval: 900, params: { id: ROWID, name: Name, email: Email, message: Message, birthday: BirthDay } } }; try { const yearlyCronDetails = await jobScheduling.CRON.createCron(cronDetail); console.log('Yearly cron created:', yearlyCronDetails); } catch (error) { console.error('Error scheduling cron job:', error); throw error; } } Notes: * 15、33、47、64、および85行目に**テーブルID**を入力してください。この値はData Storeで作成したテーブルから取得できます。 * 245、246、および248行目にジョブメタの詳細を入力してください。これらの値はJob SchedulingのJob Poolセクションから取得できます。 Advanced I/O functionのコーディングに使用したAPIを確認しましょう。これらのAPIは、サーバー、Data Store、およびJob Schedulingサービス間のルーティングを処理します: * POST /insertReminder:クライアントで入力された詳細を使用して、Data Storeに新しいリマインダーを追加します。また、タスクを自動化するためにCatalyst Job Schedulingサービスを使用してDynamic Cronジョブをスケジュールします。 * GET /getReminder:Data Storeに保存されているすべてのリマインダーを取得し、レスポンスとして返します。 * PUT /updateReminder:クライアントで入力された詳細を使用して、Data Store内の既存のリマインダーを更新します。 * PATCH /toggleAutoSend:クライアント側でユーザーが設定した設定に基づいて、リマインダーの自動送信ステータスを切り替えます。 * DELETE /deleteReminder/:id:提供されたreminder IDを使用して、Data StoreおよびCronから特定のリマインダーを削除します。 -------------------------------------------------------------------------------- title: "Job Functionのコーディング" description: "Job Schedulingサービスを使用して誕生日グリーティングアプリケーションを構築し、指定した相手にメールで自動的に誕生日の挨拶を送信します。" last_updated: "2026-07-02T09:34:22.099Z" source: "https://docs.catalyst.zoho.com/ja/tutorials/birthday-greetings/nodejs/code-job-func/" service: "All Services" related: - Job Functions (/ja/serverless/help/functions/job-functions/) - Job Scheduling SDK (/ja/sdk/nodejs/v2/job-scheduling/overview/) - Job Scheduling API (/ja/api/code-reference/job-scheduling/jobpool/get-all-jobpool/#GetAllJobPools) -------------------------------------------------------------------------------- # Job Functionのコーディング 次に、Function Job PoolからFunction Jobが実行される際にトリガーされるJob Functionをコーディングします。このファンクションは、指定した相手にメールで誕生日グリーティングを送信するために使用されます。このファンクションはMail SDKを使用します。 ファンクションディレクトリ内のJob Function BirthdayGreetings/functions/dynamic_cron/には以下が含まれます: * index.jsメインファンクションファイル * catalyst-config.json設定ファイル * Nodeモジュール * package.jsonおよびpackage-lock.json依存関係ファイル。 index.jsファイルにコードを追加します。 以下のコードをコピーしてindex.jsファイルに貼り付けてください。 Note: このセクションのコードを十分に理解するために、必ず目を通してください。 'use strict'; const catalyst = require('zcatalyst-sdk-node'); module.exports = async (jobRequest, context) => { const catalystApp = catalyst.initialize(context); try { console.log("Jobs", jobRequest.getAllJobParams()); const { id, name, email, message, birthday } = jobRequest.getAllJobParams(); await catalystApp.email().sendMail({ from_email: 'emmy@zylker.com', // 送信者のメールアドレスを追加 to_email: [email], html_mode: true, subject: `Birthday Wishes for ${name}`, content: `Hello ${name},<br><br>${message}`, }); console.log(`Email sent successfully to ${email}`); context.closeWithSuccess('Email sent successfully.'); } catch (error) { console.error('Error:', error); context.closeWithFailure('Failed to send email.'); } }; Note: 15行目に、Mailコンポーネントで設定した送信者メールアドレスを追加してください。 アプリケーションに必要なすべてのファンクションの設定が完了しました。 -------------------------------------------------------------------------------- title: "クライアントの設定" description: "Job Schedulingサービスを使用して誕生日グリーティングアプリケーションを構築し、指定した相手にメールで自動的に誕生日の挨拶を送信します。" last_updated: "2026-07-02T09:34:22.099Z" source: "https://docs.catalyst.zoho.com/ja/tutorials/birthday-greetings/nodejs/config-client/" service: "All Services" related: - クライアントディレクトリ (/ja/cli/v1/project-directory-structure/client-directory) -------------------------------------------------------------------------------- # クライアントの設定 クライアントコンポーネントを設定しましょう。 Reactウェブクライアントディレクトリには以下のファイルが含まれています: - クライアントのルートディレクトリにはclient-package.jsonファイルが含まれており、クライアントコンポーネントの名前、バージョン、デフォルトのホームページ、およびリダイレクトページを定義する設定ファイルです。 - birthdaygreetingsクライアントディレクトリには、Reactアプリのデフォルトプロジェクト構造に従い、2つのサブフォルダが含まれています: - publicフォルダは一般的に、ウェブアプリのアイコンファイルやindex.htmlファイルなど、パブリックURLを通じてブラウザから公開アクセスできるファイルを格納するために使用されます。 - srcフォルダには、Reactアプリをコンパイルする際にビルドフォルダに含まれるアプリケーションのソースファイルが含まれています。 - クライアントディレクトリには、package.json依存関係ファイルと隠し.gitignoreファイルも含まれています。 BirthdayGreetings/birthdaygreetings/publicディレクトリのファイルには以下が含まれます: - Tests.js、index.js、reportWebVitals.js、logo.svg、App.test.jsなどのネイティブReactファイル。 - index.html CatalystコンポーネントにアクセスするためのCatalyst Web SDKが含まれています。 - さらに、以下のファイルを作成します: - embeddediframe.css:アプリケーションのログインフォームに必要なスタイリング要素が含まれます。 - fpwd.css:アプリケーションのパスワード忘れ画面に必要なスタイリング要素が含まれます。 BirthdayGreetings/birthdaygreetings/srcディレクトリのファイルには以下が含まれます: - setupTests.js、reportWebVitals.js、logo.svg、App.test.jsなどのネイティブReactファイル。 - App.js:アプリケーションのハッシュルートが含まれます。 - App.css:アプリケーション全体のスタイリング要素が含まれます。 - index.js:アプリケーションのエントリーポイントとして機能します。 - index.css:index.jsファイルに存在する要素のスタイリングが含まれています。 - srcディレクトリに以下の追加ファイルを作成します: - Layout.js:アプリケーションのロジック用Reactコンポーネントが含まれます。 - LoginPage.js:Catalystコンソールで生成したログインページ用Reactコンポーネントが含まれます。 - UserProfile.js:トップバーとサーバーへのリマインダーデータ送信フォームを表示するユーザープロフィールページが含まれます。 - UserProfile.css:カスタムスクロールバーの設定やヘッダーセクションのレイアウト調整を含む、ユーザープロフィールページのスタイリング要素が含まれます。 - Signup.js:Catalystコンソールで生成したサインアップページ用Reactコンポーネントが含まれます。 - Signup.css:アプリケーションのサインアップページのスタイリング要素が含まれます。 - ListUsers.js:設定したすべてのリマインダーを一覧表示し、編集や削除を可能にし、Axiosリクエストを使用してリマインダーのステータスを切り替える機能を提供するページが含まれます。 - さらに、srcディレクトリにcomponentsというフォルダを作成する必要があります。BirthdayGreetings/birthdaygreetings/src/components/には以下のコードファイルが含まれます: - form.jsx:フォームデータの管理、入力変更の処理、onSubmit関数を通じて親コンポーネントへのフォームデータ送信を担当するフォームコンポーネントが含まれます。 - modal.jsx:名前、メッセージ、誕生日、メールの入力フィールドと更新ボタンを含む、ユーザーデータ編集用のモーダル機能が含まれます。 - modal.css:バックドロップと中央揃えのコンテンツを持つ、ユーザーデータ編集用のモーダルコンポーネントのレイアウト、入力フィールド、ボタンのスタイリングが含まれます。 - ToggleSwitch.jsx:機能の有効化や無効化など、2つの状態を切り替えるトグルスイッチコンポーネントのロジックが含まれます。 - toggleSwitch.css:チェック状態と未チェック状態のトランジション効果を持つスライドトグルスイッチのスタイリングが含まれます。 - TopBar.jsx:「Add User」や「List User」などのページへのリンク、中央のタイトル、右側の「Sign-out」ボタンを含むトップナビゲーションバーの構造が含まれます。 - topBar.css:レイアウト、ボタンの外観、スティッキーポジショニングを含む、トップナビゲーションバーのスタイリング要素が含まれます。 - UserList.jsx:各リマインダーのトグル、編集、削除オプションを含むリマインダーリストのレンダリング構造が含まれます。 - Userlist.css:レイアウト、アイテムのスタイリングを含む、ユーザーリストのスタイリング要素が含まれます。また、編集ボタンと削除ボタンのデザインも含まれます。 index.html、embeddediframe.css、fpwd.css、App.js、App.css、index.css、index.js、Layout.js、LoginPage.js、UserProfile.css、UserProfile.js、Signup.js、Signup.css、form.jsx、Form.css、modal.jsx、modal.css、ToggleSwitch.jsx、toggleSwitch.css、TopBar.jsx、topBar.css、UserList.jsx、およびUserlist.cssファイルのコーディングを行います。 また、アプリケーションのホームページがログインページになるようにclient-package.jsonを変更します。 ### 追加のReactパッケージのインストール ファイルにコードを追加する前に、以下のパッケージをインストールする必要があります: * react-router-dom:このパッケージはハッシュルーティングを実行するために必要です。 * axios:クライアントのPromiseをスムーズに処理するためのパッケージです。 ターミナルでBirthdayGreetings/birthdaygreetings/public/ディレクトリから以下のコマンドを実行して、react-router-domパッケージをインストールします: npm install react-router-dom <br /> ターミナルでBirthdayGreetings/birthdaygreetings/public/ディレクトリから以下のコマンドを実行して、axiosパッケージをインストールします: npm install axios <br /> 必要なファイルを作成し、不要なファイルを削除した後のプロジェクトディレクトリです。 <br /> 以下のコードをコピーして、IDEを使用してクライアントディレクトリ(birthdaygreetings/public/)にある対応するファイルに貼り付け、ファイルを保存してください。 &lt;!DOCTYPE html&gt; &lt;html lang="en"&gt; &lt;head&gt; &lt;meta charset="utf-8" /&gt; &lt;link rel="icon" href="%PUBLIC_URL%/favicon.ico" /&gt; &lt;meta name="viewport" content="width=device-width, initial-scale=1" /&gt; &lt;meta name="theme-color" content="#000000" /&gt; &lt;meta name="description" content="Web site created using create-react-app" /&gt; &lt;link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /&gt; &lt;link rel="manifest" href="%PUBLIC_URL%/manifest.json" /&gt; &lt;script src="https://static.zohocdn.com/catalyst/sdk/js/4.5.0/catalystWebSDK.js"&gt; &lt;/script&gt; &lt;script src="/__catalyst/sdk/init.js"&gt; &lt;/script&gt; &lt;title&gt;React App&lt;/title&gt; &lt;/head&gt; &lt;body&gt; &lt;noscript&gt;You need to enable JavaScript to run this app.&lt;/noscript&gt; &lt;div id="root"&gt; &lt;/div&gt; &lt;/body&gt; &lt;/html&gt; @font-face { font-family: "Roboto Mono"; font-weight: 400; font-style: normal; src: url("https://webfonts.zohowebstatic.com/robotomonoregular/font.eot"); src: url("https://webfonts.zohowebstatic.com/robotomonoregular/font.eot?#iefix") format("eot"), url("https://webfonts.zohowebstatic.com/robotomonoregular/font.woff2") format("woff2"), url("https://webfonts.zohowebstatic.com/robotomonoregular/font.woff") format("woff"), url("https://webfonts.zohowebstatic.com/robotomonoregular/font.ttf") format("truetype"), url("https://webfonts.zohowebstatic.com/robotomonoregular/font.svg#RobotoMono-Regular") format("svg"); } @font-face { font-family: "LatoLgt"; font-weight: 300; font-style: normal; src: url("https://webfonts.zohowebstatic.com/latolight/font.eot"); src: url("https://webfonts.zohowebstatic.com/latolight/font.eot?#iefix") format("eot"), url("https://webfonts.zohowebstatic.com/latolight/font.woff2") format("woff2"), url("https://webfonts.zohowebstatic.com/latolight/font.woff") format("woff"), url("https://webfonts.zohowebstatic.com/latolight/font.ttf") format("truetype"), url("https://webfonts.zohowebstatic.com/latolight/font.svg#Lato-Light") format("svg"); } @font-face { font-family: "LatoReg"; font-weight: 400; font-style: normal; src: url("https://webfonts.zohowebstatic.com/latoregular/font.eot"); src: url("https://webfonts.zohowebstatic.com/latoregular/font.eot?#iefix") format("eot"), url("https://webfonts.zohowebstatic.com/latoregular/font.woff2") format("woff2"), url("https://webfonts.zohowebstatic.com/latoregular/font.woff") format("woff"), url("https://webfonts.zohowebstatic.com/latoregular/font.ttf") format("truetype"), url("https://webfonts.zohowebstatic.com/latoregular/font.svg#Lato-Regular") format("svg"); } @font-face { font-family: "LatoSB"; font-weight: 700; font-style: normal; src: url("https://webfonts.zohowebstatic.com/latobold/font.eot"); src: url("https://webfonts.zohowebstatic.com/latobold/font.eot?#iefix") format("eot"), url("https://webfonts.zohowebstatic.com/latobold/font.woff2") format("woff2"), url("https://webfonts.zohowebstatic.com/latobold/font.woff") format("woff"), url("https://webfonts.zohowebstatic.com/latobold/font.ttf") format("truetype"), url("https://webfonts.zohowebstatic.com/latobold/font.svg#Lato-Bold") format("svg"); } @font-face { font-family: "LatoB"; font-weight: 900; font-style: normal; src: url("https://webfonts.zohowebstatic.com/latoblack/font.eot"); src: url("https://webfonts.zohowebstatic.com/latoblack/font.eot?#iefix") format("eot"), url("https://webfonts.zohowebstatic.com/latoblack/font.woff2") format("woff2"), url("https://webfonts.zohowebstatic.com/latoblack/font.woff") format("woff"), url("https://webfonts.zohowebstatic.com/latoblack/font.ttf") format("truetype"), url("https://webfonts.zohowebstatic.com/latoblack/font.svg#Lato-Black") format("svg"); } @font-face { font-family: "signinicon"; src: url(/images/signinicon.eot); src: url(/images/signinicon.eot) format("embedded-opentype"), url(/__catalyst/auth/static-file?file_name=signinicon.woff2) format("woff2"), url(/__catalyst/auth/static-file?file_name=signinicon.ttf) format("truetype"), url(/__catalyst/auth/static-file?file_name=signinicon.woff) format("woff"), url("../images/fonts/signinicon.651f82b8f8f51f33cd0b6331f95b5b4f.svg") format("svg"); font-weight: normal; font-style: normal; font-display: block; } [class^="icon-"], [class*=" icon-"] { /* use !important to prevent issues with browser extensions that change fonts */ font-family: "signinicon" !important; speak: never; font-style: normal; font-weight: normal; font-variant: normal; text-transform: none; line-height: 1; /* Better Font Rendering =========== */ -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } .icon-hide:before { content: "\e907"; } .icon-show:before { content: "\e914"; } .icon-backarrow:before { content: "\e900"; } .icon-backup:before { content: "\e901"; } .icon-support:before { content: "\e915"; } body { font-family: "Roboto", sans-serif; margin: 0px; } .bg_one { display: block; position: fixed; top: 0px; left: 0px; height: 100%; width: 100%; background: url("../images/bg.49756b7c711696d95133fa95451f8e13.svg") transparent; background-size: auto 100%; z-index: -1; } .signin_container { height: 25rem; display: block; width: 520px; background-color: #fff; box-shadow: 0px 2px 30px #ccc6; margin: auto; position: relative; z-index: 1; margin-top: auto; overflow: hidden; } .mod_container { width: 500px; } .checkbox_label { display: inline-block; font-size: 14px; margin: 0px 6px; float: left; cursor: pointer; line-height: 20px; } .signin_box { min-height: 520px; height: auto; background: #fff; box-sizing: border-box; border-radius: 2px; transition: all 0.1s ease-in-out; float: left; overflow-y: auto; display: table-cell; border-right: 2px solid #f1f1f1; width: 100%; padding: 0px 30px; height: auto; border-right: none; } .nopassword_message { display: table-cell; font-size: 14px; color: #222222; letter-spacing: 0px; line-height: 20px; height: auto; width: auto; margin-left: 10px; font-weight: 400; padding-left: 10px; } .portal_logo { display: block; height: 30px; width: auto; margin-bottom: 20px; background-size: auto 100%; } .pointer { cursor: pointer; } #forgotpassword { cursor: default; } #forgotpassword > a { cursor: pointer; } .text16 { display: block; text-align: center; margin-bottom: 30px; color: #626262; font-size: 16px; font-weight: 500; text-decoration: none; } .checkbox { float: left; z-index: 0; height: 12px; width: 12px; display: inline-block; border: 2px solid #ccc; border-radius: 2px; position: relative; top: 1.5px; overflow: hidden; background-color: #fff; } .show_hide_password { font-size: 34px; color: #a7a7a7; position: relative; top: -39px; right: 13px; float: right; cursor: pointer; height: 20px; } .pass_policy, .pass_policy_error { color: #8c8c8c; font-size: 14px; padding-top: 10px; } .pass_policy_error { color: #ed7500; display: block; } .checkbox_check { position: absolute; z-index: 1; opacity: 0; left: 50px; margin: 0px; height: 16px; cursor: pointer; width: 16px; } .checkbox_div { display: none; height: 16px; width: auto; margin-bottom: 30px; } .checkbox_div { display: block; } #terminate_session_form .checkbox_mod { margin-top: 50px; } .checkbox_tick { display: block; height: 3px; width: 8px; border-bottom: 2px solid #fff; border-left: 2px solid #fff; transform: rotate(-45deg); margin: 2px 1px; } .passexpsuccess { display: none; height: 106px; width: 178px; background: url("../images/passexpiry.db4c66debd4dd8880655f338ead6b973.png") no-repeat transparent; background-size: auto 100%; margin: 0px auto; margin-bottom: 20px; } .signin_head { display: block; font-size: 24px; font-weight: 500; margin-bottom: 30px; line-height: 30px; transition: all 0.1s ease-in-out; } .rightside_box { width: 390px; height: auto; float: right; box-sizing: border-box; padding: 40px; background-color: #fff; display: table-cell; } .service_name, .backup_desc, .pass_name { display: block; font-size: 16px; color: #000; font-weight: 400; } .titlename { display: block; font-size: 24px; color: #000; font-weight: 400; text-transform: capitalize; width: auto; margin-bottom: 20px; } .extramargin { margin-top: 10px; } .show_hide_password { font-size: 34px; color: #a7a7a7; position: relative; top: -39px; right: 13px; float: right; cursor: pointer; height: 20px; } .textbox_div { display: block; margin-bottom: 30px; position: relative; } .textbox_label { display: block; font-size: 14px; color: #626262; padding-bottom: 10px; } .textbox { display: block; width: 100%; height: 44px; box-sizing: border-box; border-radius: 2px; text-indent: 12px; font-size: 16px; outline: none; border: none; padding-right: 12px; transition: all 0.2s ease-in-out; background: #f1f1f1; border: 1px solid #e4e4e4; } .textbox:valid { border: 2px solid #f4f6f8; } .textindent42 { text-indent: 36px; } .textindent62 { text-indent: 56px; } .textintent52 { text-indent: 46px; } ::-webkit-credentials-auto-fill-button { visibility: hidden; pointer-events: none; position: absolute; right: 0px; } input[type="text"], input[type="password"], input[type="email"], input[type="number"] { -webkit-appearance: none; -moz-appearance: none; appearance: none; } .errorborder:focus, .textbox.errorborder { border-bottom: 2px solid #ff8484; } .btn { cursor: pointer; display: block; width: 100%; height: 44px; border-radius: 4px; letter-spacing: 0.5; font-size: 14px; font-weight: 600; outline: none; border: none; margin: auto; text-transform: uppercase; margin-bottom: 30px; transition: all 0.2s ease-in-out; } .signupbtn { width: 250px; margin-left: 0px; } .blue { box-shadow: 0px 2px 2px #fff; background-color: #159aff; color: #fff; } .grey { background-color: #f3f3f3; color: #3a3a3a; letter-spacing: 0px; } .green { box-shadow: 0px 2px 2px #fff; background-color: #00c573; color: #fff; } .blue:hover { background-color: #0966c2; cursor: pointer; box-shadow: 0px 2px 2px rgba(90, 183, 254, 0.2); } .changeloadbtn { display: block; height: 44px; width: 44px; border-radius: 22px; padding: 0px; } .changeloadbtn:before { content: ""; height: 20px; width: 20px; display: inline-block; margin: 9px; border: 3px solid #fff; border-top: 3px solid #5ab7fe; border-radius: 50%; animation: spin 0.5s linear infinite; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .hellouser { margin-bottom: 30px; } .username { display: inline-block; max-width: 100%; font-size: 16px; font-weight: 500; line-height: 24px; overflow: hidden; text-overflow: ellipsis; padding-right: 10px; float: left; } .Notyou { display: inline-block; font-size: 16px; line-height: 24px; } .signup_text { text-align: center; margin: 20px 0px; font-size: 16px; } .backbtn { display: none; float: left; height: 28px; width: 28px; border-radius: 50%; position: absolute; padding: 0px 5px; cursor: pointer; overflow: hidden; transition: all 0.1s ease; } .zeroheight { width: 0px; height: 0px; overflow: hidden; display: block; } #captcha, #bcaptcha { background: #fff; width: 60%; display: inline-block; } #captcha_img, #bcaptcha_img { width: 40%; float: right; background-color: #ffffff; border-left: none; } #captcha_img img, #bcaptcha_img img { height: 40px; } .reloadcaptcha { float: right; clear: both; margin-top: -37px; height: 30px; width: 30px; border-radius: 50px; background: url("../images/reload.eaef7ea18b680bc07558164c918909a6.png") no-repeat transparent 5px; background-size: 50%; display: inline-block; cursor: pointer; } #captcha_container { display: none; } .options { display: block; width: 100%; max-width: 500px; min-height: 80px; padding: 0px 50px; cursor: pointer; } .options:hover { background-color: #f9f9f9; } .option_details { display: inline-block; height: auto; width: 320px; margin-left: 15px; } .option_title { display: block; font-size: 16px; font-weight: 500; } .img_option { display: table-cell; height: 24px; width: 24px; color: #499aff; font-size: 24px; vertical-align: top; } .errorlabel { color: #e92b2b; } .fielderror { display: none; font-size: 14px; margin-top: 10px; } .fielderror a { text-decoration: none; color: #309ff4; } .bluetext { color: #309ff4; cursor: pointer; } #country_code_select { width: 50px; height: 40px; position: absolute; opacity: 0; display: none; z-index: 0; } .select_country_code { width: 50px; height: 40px; display: inline-block; float: left; position: absolute; line-height: 40px; text-align: center; font-size: 16px; color: black; display: none; z-index: 0; } .pic { width: 20px; height: 14px; background-size: 280px 252px; background-image: url("../images/Flags2x.0b8394efb0ea9167cef2465fb8f63d78.png"); float: left; } .cc { float: right; color: #aeaeae; } .cn { margin-left: 10px; } .select2-results__option--highlighted { background: #f4f6f8; } .searchparent { height: auto; } .searchlbl { font-size: 10px; font-weight: bolder; display: inline-block; margin-top: 15px; margin-bottom: 10px; margin-left: 10px; } .cntrysearch { width: 380px; height: 32px; border-radius: 2px; font-size: 14px; box-sizing: border-box; padding: 8px 10px; background: #f7f7f7; outline: none; border: none; margin-top: 10px; } .select2-results__option { list-style-type: none; height: 40px; box-sizing: border-box; padding: 12px 20px; font-size: 14px; line-height: 16px; } .select2-search__field { width: 380px; height: 32px; border: none; outline: none; background: #f7f7f7; border-radius: 2px; margin: 10px 0 0 10px; font-size: 14px; padding: 10.5px 8px; } .select2-selection { display: inline-block; outline: none; /* background-color: #F4F6F8; */ height: 40px; text-align: center; padding: 10px; box-sizing: border-box; cursor: pointer; } .selection { transition: all 0.2s ease-in-out; -webkit-user-select: none; display: inline-block; white-space: nowrap; overflow: hidden; width: 0px; height: 43px; } #select2-combobox-results { padding-left: 0px; max-height: 200px; overflow-y: scroll; overflow-x: hidden; width: 400px; margin-top: 10px; margin-bottom: 0px; background: white; } .select2-container--open { z-index: 10; background: #ffffff; box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.13); width: auto; box-sizing: border-box; } .select2 { position: absolute; background: transparent; box-shadow: none; display: none; margin: 2px; } .select2-results__options { overflow-y: auto; max-height: 200px; } .selection { width: auto; width: -moz-fit-content; width: -webkit-fit-content; margin: auto; display: block; } .select2-search–-hide, .select2-selection__clear { display: none; } #otp_container, #enableotpoption, #mfa_ga_container, #mfa_otp_container, #mfa_totp_container, #recoverybtn, #recovery_container, #enableforgot, #enablesaml, .trustbrowser_ui, #backup_ver_container, #backup_container, #enablemore, .password_expiry_container, .terminate_session_container { display: none; } .errorlabel { color: #e92b2b; } .fielderror { display: none; font-size: 14px; margin-top: 10px; } .field_error { color: #f37272; font-size: 14px; padding-top: 10px; } #recoverybtn, #problemsignin, .tryanother { position: absolute; left: 0px; right: 0px; bottom: 40px; } .textbox_actions, .textbox_actions_saml, .textbox_actions_more { display: block; margin-top: 20px; height: 20px; } .bluetext_action { display: inline-block; float: left; font-size: 14px; color: #159aff; font-weight: 500; line-height: 16px; cursor: pointer; } .bluetext_action a { text-decoration: none; } .bluetext_action_right { float: right; } .textbox_actions { display: block; margin-top: 20px; height: 20px; } .Alert, .Errormsg { display: block; margin: auto; height: auto; min-width: 200px; width: fit-content; width: -moz-fit-content; max-width: 600px; background-color: #032239; border-radius: 6px; position: fixed; top: -100px; left: 0px; right: 0px; transition: all 0.2s ease; padding: 0px 20px; z-index: 2; } .tick_icon, .error_icon { display: inline-block; height: 20px; width: 20px; background-color: #22c856; border-radius: 50%; background-size: 60px; margin: 15px; float: left; } .tick_icon:after { display: block; content: ""; height: 5px; width: 9px; border-bottom: 2px solid #fff; border-left: 2px solid #fff; transform: rotate(-45deg); margin: 7px 6px; box-sizing: border-box; } .alert_message, .error_message { display: inline-block; font-size: 14px; color: #fff; line-height: 18px; margin: 16px 0px; margin-right: 20px; max-width: 510px; } .error_icon { background-color: #ff676f; } .error_icon:before, .error_icon:after { position: absolute; left: 44px; top: 20px; content: " "; height: 10px; width: 2px; background-color: #ffffff; } .error_icon:before { transform: rotate(45deg); } .error_icon:after { transform: rotate(-45deg); } .select2-results__options { padding-left: 0px; } .showcountry_code { width: 62px !important; } .textindent70 { text-indent: 70px; } .countrybox { width: 400px; height: 220px; overflow: auto; } .focusmanage { position: absolute; z-index: 10; width: 400px; height: 266px; box-sizing: border-box; padding: 10px; margin-top: -25px; background: #ffffff; box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.13); border-radius: 2px; } .recoveryhead { margin-bottom: 10px; } .nopassword_container { display: none; background: #ebf4fb; border: 1px solid #cbe8ff; border-radius: 5px; width: 400px; height: auto; position: absolute; bottom: 50px; box-sizing: border-box; padding: 20px; } .nopassword_icon { display: table-cell; margin: 0px; height: 30px; width: 30px; color: #0091ff; font-size: 30px; vertical-align: middle; } .mailsearch { width: 400px; height: 30px; padding: 0px 0px; border: none; position: absolute; } .bolder1 { font-weight: bolder; } #ui-id-1 { height: 200px; width: auto; outline: none; border: none; overflow-x: hidden; overflow-y: scroll; } .ui-menu-item-wrapper { overflow: auto; } .nonclickelem { color: #626262; pointer-events: none; } .trustdevicebox { width: 500px; min-height: auto !important; } .trustdevice { width: auto; float: left; margin-right: 18px; min-width: 100px; margin-bottom: 0px; } .loadwithbtn { display: inline-block; height: 22px; width: 22px; border: 3px solid #fff; border-radius: 50%; position: absolute; margin: -2px; box-sizing: border-box; border-left: 3px solid transparent; animation-name: rotate1; animation-iteration-count: infinite; animation-duration: 1s; animation-timing-function: linear; } @keyframes rotate1 { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } .waitbtn .waittext, .loadbtntext { display: inline-block; text-indent: 25px; } #waitbtn { display: none; } .loadwithbtn:before { display: inline-block; content: ""; height: 22px; width: 22px; border: 3px solid #ffffff30; border-radius: 50%; margin: -3px -11px; position: absolute; box-sizing: border-box; } .notnowbtn .loadwithbtn { border: 3px solid #3a3a3a; border-left: 3px solid transparent; } .trustdevice .loadwithbtn { display: none; } #recoverybtn, #problemsignin, .tryanother { display: none; bottom: 50px; width: fit-content; margin: 0px auto; cursor: pointer; } #recoverOption, #verify_totp_container, #verify_qr_container { display: none; } .backoption { width: 30px; height: 30px; font-size: 24px; display: inline-block; float: left; color: #666666; padding: 3px; box-sizing: border-box; margin-right: 5px; border-radius: 15px; cursor: pointer; } .backoption:hover { background: #f4f4f4; } .rec_head_text, .backoption { display: table-cell; } .options, .optionstry { display: table; width: 100%; height: auto; padding: 20px 50px; box-sizing: border-box; cursor: pointer; } .options:hover, .optionstry:hover { background-color: #f9f9f9; } .option_details, .option_details_try { height: auto; width: auto; padding-left: 10px; vertical-align: top; box-sizing: border-box; } .option_title_try { display: block; font-size: 16px; font-weight: 500; } .option_description { display: block; font-size: 13px; line-height: 20px; color: #696969; margin-top: 5px; } .img_option_try { margin: 12px 0px; } .img_option { font-size: 30px; color: #499aff; } .option_details { display: table-cell; } .problemsignincon, .recoverymodes, .addaptivetfalist, #recoverymodeContainer { width: auto; margin: 0px -50px; } .line { background-color: #f1f1f1; width: 100%; height: 2px; border-radius: 1px; margin-bottom: 20px; } .terminate_session_container .signin_head { margin-bottom: 20px; } #terminate_session_form .checkbox_div { position: relative; margin-bottom: 10px; } #terminate_session_form .checkbox_mod { margin-top: 30px; } #terminate_session_form .checkbox_check { left: 15px; } #terminate_session_form .checkbox_check:checked ~ .checkbox { background-color: #159aff; border-color: #159aff; } #terminate_session_form .showOneAuthLable { border: 1px solid #e7e7e7; border-radius: 10px 10px 0px 0px; box-sizing: border-box; max-width: 420px; height: auto; overflow: auto; margin-bottom: 0px; } .oneAuthLable { display: inline-flex; max-width: 420px; padding: 15px; align-items: center; margin-bottom: 10px; box-sizing: border-box; border: 1px solid #e7e7e7; border-top: none; border-radius: 0px 0px 15px 15px; } .oneauth_icon { display: inline-block; width: 40px; height: 40px; border-radius: 14px; background-size: 40px 40px; } .one_auth_icon_v2 { background-image: url(../images/oneAuth2.4b2a6945d5ecd70b703ba18f5f11eb68.png); } .oneAuthLable .text_container { flex: 1; margin: 0px 10px; } .oneAuthLable .text_header { font-size: 12px; font-weight: 500; margin-bottom: 4px; } .oneAuthLable .text_desc { font-size: 10px; line-height: 14px; } .oneAuthLable .togglebtn_div { height: 16px; padding: 4px; width: 30px; display: inline-block; position: relative; } .oneAuthLable .real_togglebtn { cursor: pointer; display: inline-block; position: relative; height: 16px; width: 30px; z-index: 1; opacity: 0; position: absolute; margin: 0px; } .real_togglebtn:checked ~ .togglebase { background-color: #10bc83; } .real_togglebtn:checked ~ .togglebase .toggle_circle { left: 16px; } .oneAuthLable .togglebase { height: 16px; width: 30px; display: inline-block; background: #ccc; border-radius: 10px; position: absolute; } .oneAuthLable .toggle_circle { transition: all 0.2s ease-in-out; height: 12px; width: 12px; background-color: #fff; border-radius: 10px; display: inline-block; position: absolute; left: 2px; top: 2px; box-shadow: 0px 0px 5px #999; } @media only screen and (-webkit-min-device-pixel-ratio: 2), not all, not all, not all, only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { .google_icon { background-color: #fff; box-shadow: 0px 0px 2px #00000012, 0px 2px 2px #00000024; } .google_icon .fed_icon { background: url("../images/signin_icons@2x.c0c72f3600a6f5ec03c9119d5a7d0518.png") no-repeat transparent; background-size: 205px auto; background-position: -10px -10px; } .MS_icon { background-color: #2f2f2f; } .MS_icon .fed_icon { background: url("../images/signin_icons@2x.c0c72f3600a6f5ec03c9119d5a7d0518.png") no-repeat transparent; background-size: 205px auto; background-position: -38px -10px; } .linkedin_fed_box { background-color: #0966c2; } .linkedin_fed_box .fed_icon { background: url("../images/signin_icons@2x.c0c72f3600a6f5ec03c9119d5a7d0518.png") no-repeat transparent; background-size: 205px auto; background-position: -67px -11px; } .large_box .linked_fed_icon { background-position: -9px -174px; } .fb_fed_box { background-color: #1877f2; } .fb_fed_box .fed_icon { background: url("../images/signin_icons@2x.c0c72f3600a6f5ec03c9119d5a7d0518.png") no-repeat transparent; background-size: 205px auto; background-position: -94px -10px; } .zoho_icon { background: url(../images/signin_icons@2x.c0c72f3600a6f5ec03c9119d5a7d0518.png) no-repeat transparent; background-size: 205px auto; background-position: -152px -174px; } } @media only screen and (max-width: 600px) { body { -webkit-tap-highlight-color: rgba(255, 255, 255, 0); } .nopassword_container { width: calc(100% - 60px); padding: 10px; box-sizing: border-box; } .signin_container { width: 100%; box-shadow: none; margin: 0 auto; position: relative; z-index: 1; margin-top: 40px; height: 27rem; overflow: hidden; } #captcha_img, #captcha, #bcaptcha_img, #bcaptcha { border: 2px solid #f4f6f8; border-radius: 2px; text-indent: 3px; width: 50%; outline: none; -webkit-appearance: none; -moz-appearance: none; appearance: none; } .textbox, #verify_totp { background-color: transparent; border: none; border-bottom: 2px solid #f4f6f8; text-indent: 0px; border-radius: 0px; } .textbox:focus, #verify_totp { border: none; border-bottom: 2px solid #159aff; } .errorborder:focus, .textbox.errorborder { border-bottom: 2px solid #ff8484; } .backoption { margin-bottom: 10px; } .img_option { margin: 0px 10px 0px 0px !important; } .bg_one, .line { display: none; } .signin_head { margin-bottom: 30px; } .btn { margin-top: 10px; border-radius: 4px; } .changeloadbtn { border-radius: 22px; } .more { margin-right: 0px !important; } .textindent42 { text-indent: 36px; } .textindent62 { text-indent: 56px; } .textintent52 { text-indent: 46px; } .select2-selection { background-color: #f1f9ff; } .devices .select2-selection { background-color: #f1f9ff; } .select2-search__field { width: 280px; } .applynormal { width: 46% !important; min-width: 0px; } .borderless, .borderlesstry { line-height: 12px; font-size: 14px; } .alert_message, .error_message { max-width: 300px; width: 75%; } .Alert, .Errormsg { max-width: 400px !important; padding: 0px; min-width: 300px; } .error_icon:before, .error_icon:after { left: 24px; } ::placeholder { font-weight: 500; } .mobilehide { display: none !important; } #forgotpassword { margin-bottom: 0px; } #forgotpassword a { display: inline-block; } .fielderror { position: absolute; margin-top: 5px; } .trustdevicebox { width: auto; height: auto; } #recoverybtn, #problemsignin, .tryanother { position: initial !important; margin-top: 70px !important; } .problemsignincon, .recoverymodes, #recoverymodeContainer { width: 100%; margin-left: 0px; } } .hover-tool-tip { position: absolute; top: 15px; left: 335px; background: #fff; padding: 20px; box-shadow: 0px 0px 10px #0000001a; transition: opacity 0.1s; border-radius: 5px; z-index: 9; opacity: 0; } .hover-tool-tip::after { content: ""; position: absolute; width: 0; height: 0; margin-left: -8px; top: 25px; left: 0; box-sizing: border-box; border: 6px solid black; border-color: #ffffff transparent transparent #ffffff; transform-origin: 0 0; transform: rotate(-45deg); box-shadow: -6px -6px 10px 0 #0000001a; } .hover-tool-tip.no-arrow::after { content: none; } .hover-tool-tip ul { padding: 0; margin: 0px 0px 0px 15px; list-style: none; } .hover-tool-tip p { margin: 0px 0px 10px 0px; font-size: 14px; font-weight: 500; } .hover-tool-tip ul li { font-size: 12px; display: flex; align-items: center; white-space: nowrap; line-height: 25px; } .hover-tool-tip ul li.success::before { background-color: #0dbc83; } .hover-tool-tip ul li::before { content: ""; margin-right: 10px; background-color: red; width: 8px; height: 8px; border-radius: 50%; } .titlename, .service_name { display: none !important; } .signin_head { padding-top: 20px; } #login_id { text-indent: 0 !important; } .showcountry_code { display: none !important; } @font-face { font-family: "Roboto Mono"; font-weight: 400; font-style: normal; src: url("https://webfonts.zohowebstatic.com/robotomonoregular/font.eot"); src: url("https://webfonts.zohowebstatic.com/robotomonoregular/font.eot?#iefix") format("eot"), url("https://webfonts.zohowebstatic.com/robotomonoregular/font.woff2") format("woff2"), url("https://webfonts.zohowebstatic.com/robotomonoregular/font.woff") format("woff"), url("https://webfonts.zohowebstatic.com/robotomonoregular/font.ttf") format("truetype"), url("https://webfonts.zohowebstatic.com/robotomonoregular/font.svg#RobotoMono-Regular") format("svg"); } @font-face { font-family: "LatoLgt"; font-weight: 300; font-style: normal; src: url("https://webfonts.zohowebstatic.com/latolight/font.eot"); src: url("https://webfonts.zohowebstatic.com/latolight/font.eot?#iefix") format("eot"), url("https://webfonts.zohowebstatic.com/latolight/font.woff2") format("woff2"), url("https://webfonts.zohowebstatic.com/latolight/font.woff") format("woff"), url("https://webfonts.zohowebstatic.com/latolight/font.ttf") format("truetype"), url("https://webfonts.zohowebstatic.com/latolight/font.svg#Lato-Light") format("svg"); } @font-face { font-family: "LatoReg"; font-weight: 400; font-style: normal; src: url("https://webfonts.zohowebstatic.com/latoregular/font.eot"); src: url("https://webfonts.zohowebstatic.com/latoregular/font.eot?#iefix") format("eot"), url("https://webfonts.zohowebstatic.com/latoregular/font.woff2") format("woff2"), url("https://webfonts.zohowebstatic.com/latoregular/font.woff") format("woff"), url("https://webfonts.zohowebstatic.com/latoregular/font.ttf") format("truetype"), url("https://webfonts.zohowebstatic.com/latoregular/font.svg#Lato-Regular") format("svg"); } @font-face { font-family: "LatoSB"; font-weight: 700; font-style: normal; src: url("https://webfonts.zohowebstatic.com/latobold/font.eot"); src: url("https://webfonts.zohowebstatic.com/latobold/font.eot?#iefix") format("eot"), url("https://webfonts.zohowebstatic.com/latobold/font.woff2") format("woff2"), url("https://webfonts.zohowebstatic.com/latobold/font.woff") format("woff"), url("https://webfonts.zohowebstatic.com/latobold/font.ttf") format("truetype"), url("https://webfonts.zohowebstatic.com/latobold/font.svg#Lato-Bold") format("svg"); } @font-face { font-family: "LatoB"; font-weight: 900; font-style: normal; src: url("https://webfonts.zohowebstatic.com/latoblack/font.eot"); src: url("https://webfonts.zohowebstatic.com/latoblack/font.eot?#iefix") format("eot"), url("https://webfonts.zohowebstatic.com/latoblack/font.woff2") format("woff2"), url("https://webfonts.zohowebstatic.com/latoblack/font.woff") format("woff"), url("https://webfonts.zohowebstatic.com/latoblack/font.ttf") format("truetype"), url("https://webfonts.zohowebstatic.com/latoblack/font.svg#Lato-Black") format("svg"); } @font-face { font-family: "Zoho_Puvi_Regular"; src: url("https://static.zohocdn.com/zohofonts/zohopuvi/4.0/Zoho_Puvi_Regular.eot"); src: url("https://static.zohocdn.com/zohofonts/zohopuvi/4.0/Zoho_Puvi_Regular.eot") format("embedded-opentype"), url("https://static.zohocdn.com/zohofonts/zohopuvi/4.0/Zoho_Puvi_Regular.woff2") format("woff2"), url("https://static.zohocdn.com/zohofonts/zohopuvi/4.0/Zoho_Puvi_Regular.otf") format("opentype"); font-weight: normal; font-style: normal; /* font-display: swap */ } @font-face { font-family: "signinicon"; src: url(/images/signinicon.eot); src: url(/images/signinicon.eot) format("embedded-opentype"), url(/images/signinicon.woff2) format("woff2"), url(/images/signinicon.ttf) format("truetype"), url(/images/signinicon.woff) format("woff"), url("../images/fonts/signinicon.651f82b8f8f51f33cd0b6331f95b5b4f.svg") format("svg"); font-weight: normal; font-style: normal; font-display: block; } [class^="icon-"], [class*=" icon-"] { /* use !important to prevent issues with browser extensions that change fonts */ font-family: "signinicon" !important; speak: never; font-style: normal; font-weight: normal; font-variant: normal; text-transform: none; line-height: 1; /* Better Font Rendering =========== */ -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } .icon-device:before { content: "\e90a"; } .icon-reload:before { content: "\e912"; } .icon-backarrow:before { content: "\e900"; } .icon-email:before { content: "\e904"; } .icon-hide:before { content: "\e907"; } .icon-otp:before { content: "\e90c"; } .icon-show:before { content: "\e914"; } body { margin: 0px; } ::placeholder { /* Chrome, Firefox, Opera, Safari 10.1+ */ color: #0003; } .bg_one { display: block; position: fixed; top: 0px; left: 0px; height: 100%; width: 100%; /* background: url("../images/bg.49756b7c711696d95133fa95451f8e13.svg") transparent; */ background-size: auto 100%; z-index: -1; } .titlename { display: block; font-size: 24px; color: #000; font-weight: 400; text-transform: capitalize; width: auto; margin-bottom: 20px; } .Alert, .Errormsg { display: flex; align-items: center; justify-content: center; margin: auto; height: auto; min-width: 200px; width: fit-content; width: -moz-fit-content; max-width: 600px; background-color: #032239; border-radius: 6px; position: fixed; top: -100px; left: 0px; right: 0px; transition: all 0.2s ease; padding: 0px 20px; z-index: 2; } .tick_icon, .error_icon { display: inline-block; height: 20px; width: 20px; background-color: #22c856; border-radius: 50%; background-size: 60px; margin: 0px 15px; float: left; position: relative; } .tick_icon:after { display: block; content: ""; height: 5px; width: 9px; border-bottom: 2px solid #fff; border-left: 2px solid #fff; transform: rotate(-45deg); margin: 7px 6px; box-sizing: border-box; } .alert_message, .error_message { display: inline-block; font-size: 14px; color: #fff; line-height: 18px; margin: 16px 0px; margin-right: 20px; max-width: 510px; } .error_icon { background-color: #ff676f; } .error_icon:before, .error_icon:after { position: absolute; left: 9px; top: 5px; content: " "; height: 10px; width: 2px; background-color: #ffffff; } .error_icon:before { transform: rotate(45deg); } .error_icon:after { transform: rotate(-45deg); } .loader { width: 36px; height: 36px; border: 4px solid #159aff; border-bottom: 4px solid transparent; border-radius: 50%; display: block; box-sizing: border-box; animation: rotate 1s linear infinite; position: absolute; top: 0px; z-index: 7; bottom: 0px; left: 0px; right: 0px; margin: auto; } @keyframes rotate { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .blur { top: 0px; left: 0px; bottom: 0px; background: #fff; width: 100%; height: auto; margin: auto; position: absolute; z-index: 3; opacity: 0.9; display: block; } .bold_font { font-weight: 500; } .searchparent { height: auto; } .textbox_div { display: block; margin-bottom: 30px; position: relative; } .textbox { display: block; width: 100%; height: 44px; box-sizing: border-box; border-radius: 2px; text-indent: 12px; font-size: 16px; outline: none; border: none; padding-right: 12px; transition: all 0.2s ease-in-out; background: #f9f9f9; border: 1px solid #dddddd; } .textbox:hover, .textbox:valid, .textbox:focus { border: 1px solid #b9b9b9; } #last_password { padding-right: 40px; } ::-webkit-credentials-auto-fill-button { visibility: hidden; pointer-events: none; position: absolute; right: 0px; } input[type="text"], input[type="password"], input[type="email"], input[type="number"] { -webkit-appearance: none; -moz-appearance: none; appearance: none; } .errorborder { border: 2px solid #ff8484 !important; } .textbox:-webkit-autofill, .textbox:-webkit-autofill:hover, .textbox:-webkit-autofill:focus, .textbox:-webkit-autofill:active { -webkit-box-shadow: inset 0 0 0px 9999px #f9f9f9; -webkit-text-fill-color: black; background-color: #f9f9f9 !important; } .fielderror { display: none; font-size: 14px; margin-top: 10px; } .fielderror a { text-decoration: none; color: #309ff4; } #captcha_container { display: none; border: 1px solid #dddddd; width: 250px; padding: 10px; box-sizing: border-box; margin-top: 10px; border-radius: 4px; } #captcha { width: 100%; display: inline-block; height: 40px; padding: 0px 12px; text-indent: 0px; background: #f9f9f9; } #captcha_img { background-color: #ffffff; border: none; height: 60px; width: 150px; margin-left: 20px; } #captcha_img img { height: 50px; width: 150px; box-sizing: border-box; margin: 0px; } .reloadcaptcha { height: 30px; width: 30px; border-radius: 50%; display: inline-block; cursor: pointer; position: absolute; right: 20px; top: 20px; background-color: #f2f2f2; font-size: 30px; box-sizing: border-box; color: #0006; } .reloadcaptcha:hover { color: #000000b3; } .load_captcha_btn { animation: spin 0.5s linear infinite; } #Last_password_div .head_info { margin: 0px; } .zeroheight { width: 0px; height: 0px; overflow: hidden; display: block; } .btn { cursor: pointer; display: block; width: 100%; height: 44px; border-radius: 4px; letter-spacing: 0.5; font-size: 14px; font-weight: 600; outline: none; border: none; margin: auto; text-transform: uppercase; margin-bottom: 30px; transition: width 0.2s ease-in-out; } .blue { background-color: #159aff; color: #fff; } .grey { background-color: #f3f3f3; color: #3a3a3a; letter-spacing: 0px; } .green { box-shadow: 0px 2px 2px #fff; background-color: #00c573; color: #fff; } .blue:hover { background: #118be9; } .changeloadbtn { display: block; height: 44px; width: 44px; border-radius: 22px; padding: 0px; } .changeloadbtn:before { content: ""; height: 20px; width: 20px; display: inline-block; margin: 9px; border: 3px solid #fff; border-top: 3px solid #5ab7fe; border-radius: 50%; animation: spin 0.5s linear infinite; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .doneBtn { position: relative; } .doneBtn:before { content: ""; opacity: 1; height: 15px; width: 6px; border: unset; border-radius: unset; transform-origin: left top; border-right: 3px solid #fff; border-top: 3px solid #fff; left: 12px; top: 23px; margin: 0px; position: absolute; transform: scaleX(-1) rotate(135deg); animation: tickmark 0.8s ease; } @keyframes tickmark { 0% { height: 0px; width: 0px; border-radius: unset; opacity: 1; } 20% { height: 0px; width: 6px; opacity: 1; border-radius: unset; } 40% { height: 15px; width: 6px; opacity: 1; border-radius: unset; } 100% { height: 15px; width: 6px; opacity: 1; border-radius: unset; } } .doneWithContent span:before { content: ""; opacity: 1; height: 14px; width: 5px; border: unset; border-radius: unset; transform-origin: left top; border-right: 3px solid #fff; border-top: 3px solid #fff; display: inline-block; margin: 0px; position: relative; transform: scaleX(-1) rotate(135deg); top: 12px; left: -21px; } .doneWithContent { padding-left: 21px; position: relative; } .doneWithContent:after { position: absolute; height: 100%; content: ""; display: block; left: 0px; top: 0px; background: #00000008; animation-name: btn_loading; animation-duration: 4.3s; animation-iteration-count: 1; animation-timing-function: linear; } .restrict_icon { display: block; height: 102px; width: 100%; background: url(../images/URLError.76c2ce33de912ad7ae9d2fbfd93ec2c1.png) no-repeat transparent; background-size: auto 100%; margin: auto; margin-top: 30px; background-position: center; } .error_portion { display: none; } .error_content { text-align: center; } .error_content .error_header { font-size: 20px; font-weight: 500; margin-top: 30px; line-height: 26px; } .error_content .error_desc { font-size: 16px; margin: 10px 0px 30px 0px; line-height: 24px; font-weight: 500; color: #000000cc; } .retry_button { line-height: 44px; } .doneWithContent:hover { background: #159aff; } .doneWithContent span { width: 100%; display: block; transition: width 0.2s ease-in-out; white-space: nowrap; } @keyframes btn_loading { 0% { width: 0%; } 100% { width: 100%; } } .resend_label { display: block; text-align: center; } .push_resend { font-size: 14px; color: #626262; font-weight: 500; line-height: 16px; cursor: none; } .bottom_option { cursor: pointer; width: max-content; font-size: 16px; font-weight: 500; text-decoration: none; color: #0091ff; } .bottom_line_opt { width: 100%; display: flex; justify-content: center; position: absolute; left: 0px; bottom: 40px; } .errorlabel { color: #e92b2b; } .show_hide_password { font-size: 24px; color: #00000066; position: relative; top: -34px; right: 13px; float: right; cursor: pointer; line-height: 23px; } .show_hide_password:hover { color: #000000b3; } .select2-results__option { list-style-type: none; height: 40px; box-sizing: border-box; padding: 12px 20px; font-size: 14px; line-height: 16px; } .select2-search__field { width: 380px; height: 32px; border: none; outline: none; background: #f7f7f7; border-radius: 2px; margin: 10px 8px; font-size: 14px; padding: 10.5px 8px; } .select2-selection { display: inline-block; outline: none; background-color: #f9f9f9; height: 40px; text-align: center; padding: 10px; box-sizing: border-box; cursor: pointer; } .selection { transition: all 0.2s ease-in-out; -webkit-user-select: none; display: inline-block; white-space: nowrap; overflow: hidden; width: 0px; height: 43px; } #select2-combobox-results { padding-left: 0px; max-height: 200px; overflow-y: scroll; overflow-x: hidden; width: 400px; margin-top: 10px; margin-bottom: 0px; background: white; } .select2-container--open { z-index: 10; background: #ffffff; box-shadow: 0px 5px 8px 2px #0000000d; width: auto; box-sizing: border-box; } .select2 { position: absolute; background: transparent; box-shadow: none; display: none; margin: 2px; } .select2-results__options { overflow-y: auto; max-height: 200px; } .selection { width: auto; width: -moz-fit-content; width: -webkit-fit-content; margin: auto; display: block; } .select2-search--hide, .select2-selection__clear { display: none; } #country_code_select { width: 50px; height: 40px; position: absolute; opacity: 0; display: none; z-index: 0; } .select_country_code { width: 50px; height: 40px; display: inline-block; float: left; position: absolute; line-height: 39px; text-align: center; font-size: 16px; color: black; display: none; z-index: 0; } .pic { width: 20px; height: 14px; background-size: 280px 252px; background-image: url("../images/Flags2x.0b8394efb0ea9167cef2465fb8f63d78.png"); float: left; } .cc { float: right; color: #aeaeae; } .cn { margin-left: 10px; } .select2-results__option--highlighted { background: #f4f6f8; } .searchparent { height: auto; } .select2-results__options { padding-left: 0px; } .optionstry { display: table; width: 100%; height: auto; padding: 20px 50px; box-sizing: border-box; cursor: pointer; } .optionstry:hover { background-color: #f9f9f9; } .img_option_try { margin: 12px 0px; } .img_option { display: table-cell; width: 30px; color: #499aff; font-size: 30px; vertical-align: top; height: 30px; } .option_details_try { height: auto; width: auto; padding-left: 12px; vertical-align: top; box-sizing: border-box; } .option_title_try { display: block; font-size: 16px; font-weight: 500; } .option_description { display: block; font-size: 13px; line-height: 20px; color: #000c; margin-top: 5px; } #email_confirm_div .optionstry, #mobile_confirm_div .optionstry { padding: 22px 40px; } #email_confirm_div .img_option, #mobile_confirm_div .img_option { font-size: 20px; width: 20px; height: 20px; } #email_confirm_div .option_details_try, #mobile_confirm_div .option_details_try { line-height: 20px; } .otp_container::after, .mini_txtbox:after { content: attr(placeholder); height: 54px; line-height: 54px; position: absolute; color: #b9bcbe; left: 15px; top: 0px; z-index: 1; } .mini_txtbox:after { line-height: 42px; height: 42px; } .hidePlaceHolder::after { z-index: -1 !important; } .otp_verify { margin-top: 10px; } .toggle_active { background: #f9f9f9; border-radius: 2px; } .optionmod { margin-left: 0px; box-sizing: border-box; } .option_title_try { color: #000000; } .mini_btn { margin-left: 10px; width: 30%; float: right; font-size: 12px; margin-bottom: 0px; margin-right: 0px; } .backoption { width: 30px; height: 30px; font-size: 21px; display: inline-block; float: left; color: #666666; padding: 4px 0px; box-sizing: border-box; margin-right: 5px; border-radius: 15px; cursor: pointer; font-weight: 500; text-align: center; } .backoption:hover { background: #f4f4f4; } #select_reocvery_mobile .fieldcontainer, #other_options_div .fieldcontainer, #select_reocvery_email .fieldcontainer { width: auto; margin: 0 -40px; margin-bottom: 40px; } .text16 { display: block; text-align: center; margin-bottom: 30px; color: #626262; font-size: 16px; font-weight: 500; text-decoration: none; } .pointer { cursor: pointer; } .pass_policy { color: #8c8c8c; font-size: 14px; padding-top: 10px; } .nomargin { display: block; margin: 0px; } .tryanother, .extra_options { width: fit-content; margin: 0 auto; cursor: pointer; margin-top: 20px; } .bluetext_action { display: inline-block; font-size: 14px; color: #159aff; font-weight: 500; line-height: 16px; cursor: pointer; margin-top: 5px; } .nonclickelem { color: #626262; pointer-events: none; cursor: none; } .bluetext_action a { text-decoration: none; } .recovery_container { display: block; width: 480px; height: 350px; background-color: #fff; box-shadow: 0px 2px 30px 0px #2b2b2b17; margin: auto; position: relative; z-index: 1; overflow: hidden; } .recovery_box { width: 480px; min-height: 500px; height: auto; background: #fff; box-sizing: border-box; padding: 40px 40px; border-radius: 2px; transition: all 0.1s ease-in-out; float: left; overflow-y: auto; display: table-cell; border-right: 2px solid #f1f1f1; } .menuicon { display: inline-block; float: left; height: 14px; width: 14px; padding: 14px; font-size: 14px; text-align: center; } .user_info { display: inline-flex; justify-content: space-between; border: 1px solid #eeeeee; margin-bottom: 20px; border-radius: 7px; cursor: pointer; box-sizing: border-box; max-width: 100%; flex-wrap: nowrap; } .user_info_space { margin-top: 20px; } .support_temp_info { margin-bottom: 30px; line-height: 24px; } .menutext { display: inline-block; font-size: 16px; padding: 12px 14px; line-height: 20px; width: auto; word-break: break-all; } .fieldcontainer { margin-bottom: 40px; } .change_user { position: relative; float: right; font-size: 14px; padding: 0px 14px 0px 0px; display: inline-block; color: #0091ff; line-height: 38px; font-weight: 500; display: flex; align-items: center; justify-content: center; } .info_head { display: block; font-size: 20px; font-weight: 500; margin-bottom: 20px; line-height: 30px; overflow: auto; transition: all 0.1s ease-in-out; } .info_head #headtitle { display: block; } .head_info { font-size: 16px; margin: 10px 0px 0px 0px; line-height: 24px; font-weight: 400; color: #000000e6; } .user_info_space + .head_info { margin: 0px; } .otp_container { display: block; width: 100%; height: 54px; box-sizing: border-box; border-radius: 2px; font-size: 16px; outline: none; padding: 0px 15px; transition: all 0.2s ease-in-out; background: #f9f9f9; border: 1px solid #dddddd; text-indent: 0px; } .customOtp { border: none; outline: none; background: transparent; height: 100%; font-size: 16px; text-align: left; width: 22px; padding: 0px; } .textindent42 { text-indent: 46px; } .textindent62 { text-indent: 56px; } .textintent52 { text-indent: 51px; } .box_header_load { display: block; height: auto; padding: 50px 0px; -webkit-animation-name: blink; /* Safari 4.0 - 8.0 / -webkit-animation-duration: 1s; / Safari 4.0 - 8.0 */ animation-name: blink; animation-duration: 1s; animation-iteration-count: infinite; animation-timing-function: linear; opacity: 0.05; } .box_head_load { display: block; width: 180px; height: 18px; border-radius: 8px; background-color: #000; } .blink_btn { -webkit-animation-name: blink; /* Safari 4.0 - 8.0 / -webkit-animation-duration: 1s; / Safari 4.0 - 8.0 */ animation-name: blink; animation-duration: 1s; animation-iteration-count: infinite; animation-timing-function: linear; opacity: 0.05; } .box_define_load { display: block; max-width: 800px; width: 100%; height: 16px; border-radius: 8px; background-color: #000; margin-top: 20px; } @keyframes blink { 0% { opacity: 0.08; } 50% { opacity: 0.01; } 100% { opacity: 0.08; } } #loading_div, #Last_password_div, #mobile_confirm_div, #email_confirm_div, #confirm_otp_div, #other_options_div, #contact_support_div, #change_password_div, #recovery_device_div, #password_matched_div, #username_div, #terminate_session_div { display: none; } #multiple_reocvery_mobile, #single_reocvery_mobile, #multiple_reocvery_email, #single_reocvery_email { display: none; } .verify_mob_container, .verify_email_container, .select_device_othermodes { display: none; } .devices { position: relative; padding: 0px; width: fit-content !important; width: -moz-fit-content !important; width: -webkit-fit-content !important; margin: auto; border-radius: 8px; box-sizing: border-box; margin-bottom: 30px; background: #fcfcfc; } .device_title { position: absolute; top: 12px; left: 48px; font-size: 10px; font-weight: 600; text-transform: uppercase; color: #000000b3; line-height: 14px; z-index: 11; } .select2-container--device_select { margin: 0px; display: block; position: relative; margin: auto; cursor: pointer; border: 1px solid #dddddd; border-radius: 8px; overflow: hidden; } #recovery_device_select + .select2-container { width: fit-content !important; width: -moz-fit-content !important; width: -webkit-fit-content !important; } .select2-container--device_select .select2-selection__arrow { position: absolute; top: 0px; right: 14px; width: auto; height: 100%; } .select2-container--device_select .select2-selection__arrow b { border-color: transparent #00000066 #00000066 transparent; border-style: solid; transform: rotate(45deg); border-width: 2px; height: 4px; width: 4px; position: relative; border-radius: 1px; display: inline-block; top: 24px; } .select2-container--device_select .selection { margin: auto; height: 60px; box-sizing: border-box; min-width: 140px; } .select2-container--device_select .select2-selection { height: unset; padding: 12px 34px 12px 14px; } .select2-container--device_select .select_con { font-weight: 400; margin-left: 12px; margin-top: 16px; } .select2-container--device_select .select_icon { margin-top: 7px; } .select2-results__options { margin: 0px; } .select2-results__options .select_con { margin: 0px; } .select2-dropdown .select_con { width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .hideArrow .select2-selection { cursor: default; padding: 12px 14px; } .hideArrow .select2-selection__arrow { display: none; } .secondary_devices { display: none; width: 150px; margin: auto; padding: 8px 18px; border-radius: 18px; background-color: #f4f6f8; border: none; overflow: hidden; max-width: 200px; text-overflow: ellipsis; font-size: 16px; outline: none; } .secondary_devices .select2-container--open { width: 200px; } .device_select { display: block; position: unset; width: auto !important; } .device_selection { padding: 8px 14px; border-radius: 18px; } .deviceparent { text-align: center; display: none; text-overflow: ellipsis; overflow: hidden; width: 100%; white-space: nowrap; } .deviceparent { display: block; } .deviceinfo { display: inline-block; } .devicetext { margin: 0 auto; margin-left: 30px; } .select_icon { float: left; font-size: 20; color: #499aff; } .select_con { float: left; width: auto; overflow: hidden; margin-left: 5px; text-overflow: ellipsis; } .options_selct { max-width: 180px; font-size: 14px; line-height: 20px; font-weight: 500; width: auto; } .contact_support_class { min-height: unset; } .contact_support_class .recovery_box { min-height: unset; } #mfa_totp_section { display: none; } /* Terminate_session page */ #terminate_session_form { overflow: auto; } #terminate_session_form .checkbox_div { position: relative; } #terminate_session_form .checkbox_check { position: absolute; z-index: 1; opacity: 0; left: 15px; margin: 0px; height: 16px; cursor: pointer; width: 16px; } #terminate_session_form .checkbox { float: left; z-index: 0; height: 12px; width: 12px; display: inline-block; border: 2px solid #ccc; border-radius: 2px; position: relative; top: 1.5px; overflow: hidden; background-color: #fff; } #terminate_session_form .checkbox_check:hover ~ .checkbox { border: 2px solid #159aff; } #terminate_session_form .checkbox_check:checked ~ .checkbox { background-color: #159aff; border-color: #159aff; } #terminate_session_form .checkbox .checkbox_tick { display: block; height: 3px; width: 8px; border-bottom: 2px solid #fff; border-left: 2px solid #fff; transform: rotate(-45deg); margin: 2px 1px; } #terminate_session_form .checkbox_label { cursor: pointer; margin: 0px 6px; line-height: 20px; width: 85%; display: inline-block; } #terminate_session_form .showOneAuthLable { border: 1px solid #e7e7e7; border-radius: 10px 10px 0px 0px; box-sizing: border-box; max-width: 420px; } .oneAuthLable { display: inline-flex; max-width: 420px; padding: 15px; align-items: center; box-sizing: border-box; border: 1px solid #e7e7e7; border-top: none; border-radius: 0px 0px 15px 15px; } .oneauth_icon { display: inline-block; width: 40px; height: 40px; border-radius: 14px; background-size: 40px 40px; } .one_auth_icon_v2 { background-image: url(../images/oneAuth2.4b2a6945d5ecd70b703ba18f5f11eb68.png); } .oneAuthLable .text_container { flex: 1; margin: 0px 10px; } .oneAuthLable .text_header { font-size: 12px; font-weight: 500; margin-bottom: 4px; } .oneAuthLable .text_desc { font-size: 10px; line-height: 14px; } .oneAuthLable .togglebtn_div { height: 16px; padding: 4px; width: 30px; display: inline-block; position: relative; } .oneAuthLable .real_togglebtn { cursor: pointer; display: inline-block; position: relative; height: 16px; width: 30px; z-index: 1; opacity: 0; position: absolute; margin: 0px; } .real_togglebtn:checked ~ .togglebase { background-color: #10bc83; } .real_togglebtn:checked ~ .togglebase .toggle_circle { left: 16px; } .oneAuthLable .togglebase { height: 16px; width: 30px; display: inline-block; background: #ccc; border-radius: 10px; position: absolute; } .oneAuthLable .toggle_circle { transition: all 0.2s ease-in-out; height: 12px; width: 12px; background-color: #fff; border-radius: 10px; display: inline-block; position: absolute; left: 2px; top: 2px; box-shadow: 0px 0px 5px #999; } @media only screen and (max-width: 600px) { body { -webkit-tap-highlight-color: rgba(255, 255, 255, 0); } .bg_one { display: none; } .alert_message, .error_message { max-width: 300px; width: 75%; } .Alert, .Errormsg { max-width: 400px !important; padding: 0px; min-width: 300px; } .textbox, .otp_container { background-color: transparent; border: none; border-bottom: 2px solid #f4f6f8; border-radius: 0px; } .textbox { transition: unset; } .textbox:-webkit-autofill, .textbox:-webkit-autofill:hover, .textbox:-webkit-autofill:focus, .textbox:-webkit-autofill:active { -webkit-box-shadow: inset 0 0 0px 9999px white; -webkit-text-fill-color: black; } .textbox:valid, .textbox:hover { border: none; border-bottom: 2px solid #159aff; } #captcha { border-radius: 2px; outline: none; -webkit-appearance: none; -moz-appearance: none; appearance: none; } .changeloadbtn { border-radius: 22px; } .btn { margin-top: 10px; border-radius: 4px; } .mini_btn { margin-top: 0px; } .optionstry:hover { background-color: #fff; } .img_option { margin: 0px 10px 0px 0px !important; } .option_details_try { color: #555555; display: inline-block; margin: 0px; } #multiple_reocvery_mobile .fieldcontainer, #multiple_reocvery_email .fieldcontainer, #other_options_div .fieldcontainer { margin: 0 -30px; margin-bottom: 40px; } .optionmod { margin-left: 0px; padding: 15px 30px; line-height: 24px; } .recovery_box { width: 100%; padding: 0px 30px; height: auto; border-right: none; padding-bottom: 40px; } .recovery_container { width: 100%; box-shadow: none; margin: 0 auto; position: relative; z-index: 1; margin-top: 40px; height: auto; overflow: hidden; } .info_head { margin-bottom: 30px; } } .devices .select2-container--open { background: transparent; } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min-moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { .restrict_icon { background: url(../images/URLError2x.5e762e74a83ee0cda60b8f7c9df299a8.png) no-repeat transparent center/auto 100%; } } .hide { display: none; } .hover-tool-tip { position: absolute; top: 15px; left: 335px; background: #fff; padding: 20px; box-shadow: 0px 0px 10px #0000001a; transition: opacity 0.1s; border-radius: 5px; z-index: 9; opacity: 0; } .hover-tool-tip::after { content: ""; position: absolute; width: 0; height: 0; margin-left: -8px; top: 25px; left: 0; box-sizing: border-box; border: 6px solid black; border-color: #ffffff transparent transparent #ffffff; transform-origin: 0 0; transform: rotate(-45deg); box-shadow: -6px -6px 10px 0 #0000001a; } .hover-tool-tip.no-arrow::after { content: none; } .hover-tool-tip ul { padding: 0; margin: 0px 0px 0px 15px; list-style: none; } .hover-tool-tip p { margin: 0px 0px 10px 0px; font-size: 14px; font-weight: 500; } .hover-tool-tip ul li { font-size: 12px; display: flex; align-items: center; white-space: nowrap; line-height: 25px; } .hover-tool-tip ul li.success::before { background-color: #0dbc83; } .hover-tool-tip ul li::before { margin-right: 10px; background-color: red; width: 8px; height: 8px; border-radius: 50%; } .titlename { display: none !important; } /* #lookup_div { display: none !important; } */ #lookup_div .head_info { font-size: 0; } #lookup_div .head_info::before { content: "Enter your email address"; font-size: 15px; } .info_head { padding-top: 20px; } .rec_modes_other_options { display: none !important; } .recovery_container { height: 29rem; } .Last_password_div { margin-top: -30px; } 以下のコードをコピーして、IDEを使用してクライアントディレクトリ(birthdaygreetings/src/components/)内の各ファイルに貼り付けて保存します。 .form-container { display: grid; height: 190px; grid-template-columns: 1fr 1fr; grid-template-rows: auto auto; gap: 20px; padding: 20px; max-width: 786px; margin: 75px auto; box-shadow: 0 4px 8px rgb(0 0 0 / 10%); border-radius: 8px; background-color: #fff; } .form-group { width: 100%; height: 0px; display: flex; justify-content: space-between; align-items: center; gap: 10px; margin-top: 42px; margin-bottom: 57px; } label { font-size: 19px; font-weight: bold; width: 123px; } input { font-size: 13px; padding: 8px; border: 1px solid #ccc; border-radius: 4px; flex: 1; } input:focus { border-color: #007bff; outline: none; box-shadow: 0 0 4px rgba(0, 123, 255, 0.25); } .form-container > .form-group:nth-child(1) { grid-column: 1; grid-row: 1; } .form-container > .form-group:nth-child(2) { grid-column: 2; grid-row: 1; } .form-container > .form-group:nth-child(3) { grid-column: 1; grid-row: 2; } .form-container > .form-group:nth-child(4) { grid-column: 2; grid-row: 2; } .submit-form{ height: 50px; width: 100%; margin-bottom: 64px; } .submit-btn{ background-color: #58d358; border: none; border-radius: 10px; color: #fff; font-size: 20px; height: 46px; margin-left: 42%; width: 16%; } import { useState } from "react"; import "./form.css"; function Form({ onSubmit }) { const [formData, setFormData] = useState({ name: "", birthday: "", message: "", email: "", }); const handleChange = (e) =&gt; { const { id, value } = e.target; setFormData({ ...formData, [id]: value }); }; const handleSubmit = (e) =&gt; { e.preventDefault(); onSubmit(formData); }; const today = new Date().toISOString().split("T")[0]; return ( &lt;&gt; &lt;form onSubmit={handleSubmit}&gt; &lt;div className="form-container"&gt; &lt;div className="form-group"&gt; &lt;label htmlFor="name"&gt;Name&lt;/label&gt; &lt;input type="text" id="name" value={formData.name} onChange={handleChange} /&gt; &lt;/div&gt; &lt;div className="form-group"&gt; &lt;label htmlFor="birthday"&gt;Birthday&lt;/label&gt; &lt;input type="date" id="birthday" value={formData.birthday} onChange={handleChange} max={today} /&gt; &lt;/div&gt; &lt;div className="form-group"&gt; &lt;label htmlFor="message"&gt;Message&lt;/label&gt; &lt;input type="text" id="message" value={formData.message} onChange={handleChange} /&gt; &lt;/div&gt; &lt;div className="form-group"&gt; &lt;label htmlFor="email"&gt;Email&lt;/label&gt; &lt;input type="email" id="email" value={formData.email} onChange={handleChange} /&gt; &lt;/div&gt; &lt;/div&gt; &lt;div className="submit-form"&gt; &lt;button className="submit-btn" type="submit"&gt; Submit &lt;/button&gt; &lt;/div&gt; &lt;/form&gt; &lt;/&gt; ); } export default Form; .modal { display: flex; justify-content: center; align-items: center; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); z-index: 1000; padding: 20px; box-sizing: border-box; } .modal-content { background-color: #fff; padding: 20px; border-radius: 8px; width: 100%; max-width: 500px; position: relative; box-shadow: 0px 2px 15px rgba(0, 0, 0, 0.1); } .edit-heade{ width: 100%; height: 60px; display: flex; } .modal-header h2{ padding-left: 185px; font-size: 1.5rem; margin-bottom: 20px; } .modal-close { position: absolute; top: 10px; right: 15px; font-size: 1.5rem; cursor: pointer; color: #333; } .modal-input-group { margin-bottom: 15px; } .modal-label { font-weight: 700; font-weight: bold; display: block; margin-bottom: 5px; } .modal-input { width: 89%; padding: 10px; border-radius: 5px; border: 1px solid #ccc; font-size: 1rem; } .modal-submit { text-align: center; } .modal-button { background-color: #4CAF50; color: white; padding: 10px 20px; border: none; border-radius: 5px; cursor: pointer; font-size: 1rem; transition: background-color 0.3s; } .modal-button:hover { background-color: #45a049; } import { useState } from "react"; import "./modal.css" function Modal({ editData, onUpdate, onClose }) { const [formData, setFormData] = useState(editData); const handleChange = (e) =&gt; { const { name, value } = e.target; setFormData({ ...formData, [name]: value }); }; const handleSubmit = () =&gt; { onUpdate(formData); }; const today = new Date().toISOString().split("T")[0]; return ( &lt;div className="modal"&gt; &lt;div className="modal-content"&gt; &lt;div className="edit-header"&gt; &lt;div className="modal-header"&gt; &lt;h2&gt;Edit Greeting&lt;/h2&gt; &lt;/div&gt; &lt;div className="modal-close"&gt; &lt;button onClick={onClose}&gt;&times;&lt;/button&gt; &lt;/div&gt; &lt;/div&gt; &lt;div className="modal-input-group"&gt; &lt;input name="Name" value={formData.Name} onChange={handleChange} className="modal-input" /&gt; &lt;/div&gt; &lt;div className="modal-input-group"&gt; &lt;input name="Message" value={formData.Message} onChange={handleChange} className="modal-input" /&gt; &lt;/div&gt; &lt;div className="modal-input-group"&gt; &lt;input name="BirthDay" type="date" value={formData.BirthDay} onChange={handleChange} className="modal-input" max={today} // 最大日付を今日に設定 /&gt; &lt;/div&gt; &lt;div className="modal-input-group"&gt; &lt;input name="Email" value={formData.Email} onChange={handleChange} className="modal-input" /&gt; &lt;/div&gt; &lt;div className="modal-submit"&gt; &lt;button className="modal-button" onClick={handleSubmit}&gt;Update&lt;/button&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; ); } export default Modal; .switch { display: block; position: relative; display: inline-block; width: 34px; height: 20px; } .switch input { opacity: 0; width: 0; height: 0; } .slide { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; transition: 0.4s; border-radius: 20px; } .slide:before { position: absolute; content: ""; height: 14px; width: 14px; left: 3px; bottom: 3px; background-color: white; transition: 0.4s; border-radius: 50%; } input:checked + .slide { background-color: #2196f3; } input:checked + .slide:before { transform: translateX(14px); } import "./toggleSwitch.css" function ToggleSwitch({ isChecked, onToggle }) { return ( &lt;label className="switch"&gt; &lt;input type="checkbox" checked={isChecked} onChange={(e) =&gt; onToggle(e.target.checked)} /&gt; &lt;span className="slide round"&gt; &lt;/span&gt; &lt;/label&gt; ); } export default ToggleSwitch; body{ margin: 0; padding: 0; } .topbarContainer{ height: 50px; width: 100%; background-color: rgb(58, 58, 98); display: flex; align-items: center; position: fixed; top: 0; z-index: 999; } .topbarLeft{ flex: 1 1; display: flex; align-items: center; gap: 22%; padding-left: 29px; } .naviagtion-btn{ height: 30px; background-color: white; border: none; border-radius: 7px; font-size: 14px; font-weight: 600; } .topLogo{ font-size: 24px; margin-left: 15px; color: white; cursor: pointer; } .topBarCenter{ flex: 1 1; color: white; padding-left: 8%; } .topbarRight{ flex: 0 1; display: flex; justify-content: space-between; padding-left: 24%; } .topRight-btn{ width: 74px; height: 31px; font-size: 15px; font-weight: 650; background-color: red; color: white; margin-right: 40px; border: none; border-radius: 7px; } .topbarlink{ display: flex; margin-left: 67px; color: white; } import React, { useCallback } from 'react' import "./topBar.css" import { Link } from 'react-router-dom' export default function TopBar() { const logout = useCallback(() =&gt; { window.catalyst.auth.signOut('/'); }, []); return ( &lt;&gt; &lt;div className="topbarContainer"&gt; &lt;div className="topbarLeft"&gt; &lt;Link to="/addUserPage"&gt; &lt;button className='naviagtion-btn'&gt;Add New Greetings&lt;/button&gt; &lt;/Link&gt; &lt;Link to="/ListUsers"&gt; &lt;button className='naviagtion-btn'&gt;Greetings List&lt;/button&gt; &lt;/Link&gt; &lt;/div&gt; &lt;div className="topBarCenter"&gt; &lt;div className="seacrhbar"&gt; &lt;h1&gt;Birthday Greetings&lt;/h1&gt; &lt;/div&gt; &lt;/div&gt; &lt;div className="topbarRight"&gt; &lt;button className="topRight-btn" onClick={logout}&gt;Log Out&lt;/button&gt; &lt;/div&gt; &lt;/div&gt; &lt;/&gt; ) } .users-container { display: flex; flex-direction: column; gap: 8px; } .users-lists { width: 88%; padding: 15px; border: 1px solid #ccc; border-radius: 22px; background-color: #f9f9f9; margin-left: 80px; margin-bottom: 34px; display: flex; flex-direction: column; align-items: flex-start; } .user-item { width: 100%; display: flex; align-items: center; justify-content: space-between; } .left-content { display: flex; flex-direction: column; } .left-content span { margin-bottom: 8px; } .name { font-size: 28px; font-weight: bold; } .date, .email { font-size: 16px; } .message { font-size: 20px; } .right-button { display: flex; align-items: center; gap: 20px; width: 25%; justify-content: flex-end; } .edit-btn, .delete-btn { width: 70px; height: 34px; border-radius: 10px; color: white; border: none; } .edit-btn { background-color: black; } .delete-btn { background-color: rgb(255, 0, 0); } import ToggleSwitch from "./ToggleSwitch"; import "./UserList.css"; function UserList({ users, onEdit, onDelete, onToggle }) { return ( &lt;div className="users-container"&gt; {users.map((user) =&gt; ( &lt;div className="users-lists" key={user.ID}&gt; &lt;div className="user-item"&gt; &lt;div className="left-content"&gt; &lt;span className="name"&gt;{user.Name}&lt;/span&gt; &lt;span className="date"&gt;{user.BirthDay}&lt;/span&gt; &lt;span className="message"&gt;{user.Message}&lt;/span&gt; &lt;span className="email"&gt;{user.Email}&lt;/span&gt; &lt;/div&gt; &lt;div className="right-button"&gt; &lt;ToggleSwitch isChecked={user.AutoSend} onToggle={(checked) =&gt; onToggle(user.ID, checked)} /&gt; &lt;button className="edit-btn" onClick={() =&gt; onEdit(user.ID)}&gt;Edit&lt;/button&gt; &lt;button className="delete-btn" onClick={() =&gt; onDelete(user.ID)}&gt;Delete&lt;/button&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; ))} &lt;/div&gt; ); } export default UserList; 以下のコードをコピーして、IDEを使用してクライアントディレクトリ(birthdaygreetings/src/)内の各ファイルに貼り付けて保存します。 body { margin: 0; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; background-color: #ffffff; height: inherit; } .inputs { width: 90%; padding: 15px; margin: 15px 0 22px 0; display: inline-block; border: none; background: #f1f1f1; border-radius: 8px; } h1, h2, h3, h4, h5, h6, p { margin: 1; } #login { height: 29rem; } .Header{ display: flex; align-items: center; gap: 54%; } import { HashRouter, Routes, Route, Navigate } from "react-router-dom"; import Layout from "./Layout"; import Signup from "./SignUp"; import ListUsers from "./ListUsers"; import UserProfile from "./UserProfile"; function App() { return ( &lt;HashRouter&gt; &lt;Routes&gt; &lt;Route path="/" element={&lt;Layout /&gt;} /&gt; &lt;Route path="/signup" element={&lt;Signup /&gt;} /&gt; &lt;Route path="/addUserPage" element={&lt;UserProfile /&gt;} /&gt; &lt;Route path="/ListUsers" element={&lt;ListUsers /&gt;} /&gt; &lt;Route path="" element={&lt;Navigate to="/" replace /&gt;} /&gt; &lt;Route path="*" element={&lt;Navigate to="/" replace /&gt;} /&gt; &lt;/Routes&gt; &lt;/HashRouter&gt; ); } export default App; body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } code { font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; } import "./App.css"; import React from "react"; import { createRoot } from "react-dom/client"; import App from "./App"; const container = document.getElementById("root"); const root = createRoot(container); root.render( &lt;React.StrictMode&gt; &lt;App /&gt; &lt;/React.StrictMode&gt; ); import UserProfile from "./UserProfile"; import LoginPage from "./LoginPage.js"; import { useEffect, useState } from "react"; function Layout() { const [isFetching, setIsFetching] = useState(true); const [isUserAuthenticated, setIsUserAuthenticated] = useState(false); const [userDetails, setUserDetails] = useState({ firstName: "", lastName: "", mailid: "", timeZone: "", createdTime: "", }); useEffect(() =&gt; { window.catalyst.auth.isUserAuthenticated().then((result) =&gt; { setUserDetails({ firstName: result.content.first_name, lastName: result.content.last_name, mailid: result.content.email_id, timeZone: result.content.time_zone, createdTime: result.content.created_time, }); setIsUserAuthenticated(true); }) .catch((err) =&gt; {}) .finally(() =&gt; { setIsFetching(false); }); }, []); return ( &lt;&gt; {isFetching ? ( &lt;p&gt;Loading ….&lt;/p&gt; ) : isUserAuthenticated ? ( &lt;UserProfile userDetails={userDetails} /&gt; ) : ( &lt;LoginPage /&gt; )} &lt;/&gt; ); } export default Layout; import { useState, useEffect } from "react"; import axios from "axios"; import UserList from "./components/UserList"; import Modal from "./components/modal"; import TopBar from "./components/TopBar"; function ListUsers() { const [users, setUsers] = useState([]); const [editData, setEditData] = useState(null); const [isModelOpen, setModal] = useState(false); const fetchUsers = async () =&gt; { try { const response = await axios.get("/server/advance_function/getReminder"); setUsers(response.data); } catch (error) { console.error("Error fetching users:", error); } }; useEffect(() =&gt; { fetchUsers(); }, []); const handleEdit = (id) =&gt; { const userToEdit = users.find((user) =&gt; user.ID === id); setEditData(userToEdit); setModal(true); }; const handleUpdate = async (updatedData) =&gt; { try { await axios.put("/server/advance_function/updateReminder", updatedData); alert("User updated successfully!"); setEditData(null); setModal(false); fetchUsers(); } catch (error) { console.error("Error updating user:", error); } }; const handleDelete = async (id) =&gt; { if (window.confirm("Are you sure you want to delete this birthday wish?")) { try { await axios.delete(`/server/advance_function/deleteReminder/${id}`); setUsers(users.filter((user) =&gt; user.ID !== id)); alert("User deleted successfully!"); } catch (error) { console.error("Error deleting user:", error); } } }; const handleToggle = async (ID, checked) =&gt; { const AutoSendStatus = checked ? "enable" : "disable"; try { await axios.patch("/server/advance_function/toggleAutoSend", { id: ID, status: AutoSendStatus, }); setUsers((prevUsers) =&gt; prevUsers.map((user) =&gt; user.ID === ID ? { ...user, AutoSend: checked } : user ) ); } catch (error) { console.error("Error toggling the user:", error); } }; return ( &lt;&gt; &lt;div &gt; &lt;TopBar /&gt; &lt;/div&gt; &lt;div className="userlist-div" style={{height: "calc(100vh - 60px)",margin: "60px 0 0"}}&gt; &lt;UserList users={users} onEdit={handleEdit} onDelete={handleDelete} onToggle={handleToggle} /&gt; {isModelOpen && ( &lt;Modal editData={editData} onUpdate={handleUpdate} onClose={() =&gt; setModal(false)} /&gt; )} &lt;/div&gt; &lt;/&gt; ); } export default ListUsers; import { useEffect } from "react"; import { Link } from "react-router-dom"; const LoginPage = () => { useEffect(() => { // 設定はオプションです const config = { css_url: "/app/embeddediframe.css", // ログインページのカスタマイズCSSファイルパス(未指定の場合はデフォルトCSSが適用されます) is_customize_forgot_password: false, // デフォルト値はfalseです。パスワードリセットページをカスタマイズする場合はtrueに設定してください forgot_password_id: "login", // パスワードリセットページを読み込む要素ID(デフォルト: "loginDivElementId") forgot_password_css_url: "/app/fpwd.css", // パスワードリセットページのカスタマイズCSSファイルパス(未指定の場合はデフォルトCSSが適用されます) }; window.catalyst.auth.signIn("login", config); }, []); return ( &lt;div className="container"&gt; &lt;center&gt; &lt;h1 className="title">Birthday Greetings&lt;/h1&gt; &lt;/center&gt; &lt;div id="login">&lt;/div&gt; &lt;p className="homepage"&gt; &lt;b&gt; Don't have an account?{" "} &lt;Link to="/signup" style={{ color: "blue", textDecorationLine: "underline" }}&gt; Sign-up &lt;/Link&gt;{" "} now! &lt;/b&gt; &lt;/p&gt; &lt;/div&gt; ); }; export default LoginPage; input[type="submit"] { background-color: green; color: white; width: 40%; padding: 8px; border-radius: 8px; text-align: center; border: none; cursor: pointer; } #link { font-size: medium; color: blue; } .modal-content { background-color: #c0c0c0; margin: 30px auto; } .signupfnbtn { width: 30%; padding: 8px; border-radius: 8px; background-color: green; height: 100%; } p { text-align: center; font-size: medium; font-weight: bold; } import "./SignUp.css"; import React, { useState } from "react"; function Signup() { const [displayText, setDisplayText] = useState(""); const [form, setForm] = useState({ firstName: "", lastName: "", email: "", }); const [showForm, setShowForm] = useState(true); const handleSubmit = async (event) =&gt; { event.preventDefault(); setShowForm(false); setDisplayText( "An account verification email has been sent to your email address" ); const data = { first_name: form.firstName, last_name: form.lastName, email_id: form.email, platform_type: "web", }; try { const auth = window.catalyst.auth; const signupResponse = await auth.signUp(data); if (signupResponse.status === 200) { setTimeout(() =&gt; { window.location.href = "index.html"; }, 3000); } else { alert(signupResponse.message); } } catch (error) { console.error("Error during signup:", error); alert("An error occurred during signup. Please try again."); setShowForm(true); // エラーがある場合はフォームを再表示 } }; const handleInputChange = (e) =&gt; { const { name, value } = e.target; setForm((prevForm) =&gt; ({ ...prevForm, [name]: value, })); }; return ( &lt;div id="signup" className="signup"&gt; {showForm ? ( &lt;div&gt; &lt;center&gt; &lt;img width="80px" height="80px" src="https://cdn2.iconfinder.com/data/icons/user-management/512/profile_settings-512.png" alt="User Profile" /&gt; &lt;h1&gt;User Profile Management&lt;/h1&gt; &lt;/center&gt; &lt;form onSubmit={handleSubmit} className="modal-content"&gt; &lt;center&gt; &lt;h1&gt;Sign Up&lt;/h1&gt; &lt;p&gt;Please fill this form to sign up for a new account.&lt;/p&gt; &lt;/center&gt; &lt;label htmlFor="firstName"&gt; &lt;b&gt;First Name&lt;/b&gt; &lt;input name="firstName" className="inputs" placeholder="Enter First Name" value={form.firstName} onChange={handleInputChange} required /&gt; &lt;/label&gt; &lt;label htmlFor="lastName"&gt; &lt;b&gt;Last Name&lt;/b&gt; &lt;input name="lastName" className="inputs" placeholder="Enter Last Name" value={form.lastName} onChange={handleInputChange} required /&gt; &lt;/label&gt; &lt;label htmlFor="email"&gt; &lt;b&gt;Email&lt;/b&gt; &lt;input name="email" className="inputs" placeholder="Enter email address" value={form.email} onChange={handleInputChange} required /&gt; &lt;/label&gt; &lt;p&gt; By creating an account, you agree to our{" "} &lt;a href="https://www.zoho.com/catalyst/terms.html" target="_blank" rel="noopener noreferrer" id="link" &gt; Terms & Conditions &lt;/a&gt; . &lt;/p&gt; &lt;center&gt; &lt;input type="submit" value="Sign Up" className="signupfnbtn" /&gt; &lt;/center&gt; &lt;/form&gt; &lt;/div&gt; ) : ( &lt;p&gt;{displayText}&lt;/p&gt; )} &lt;/div&gt; ); } export default Signup; .app-container{ overflow: auto; } ::-webkit-scrollbar{ width: 5px; } ::-webkit-scrollbar-track{ background-color: rgb(34, 34, 59); } ::-webkit-scrollbar-thumb{ background-color: rgb(150, 150, 150); } .Header{ display: flex; align-items: center; gap: 54%; } import "./UserProfile.css"; import axios from "axios"; import Form from "./components/form"; import TopBar from "./components/TopBar"; function UserProfile() { const handleSubmit = async (formData) =&gt; { try { await axios.post("/server/advance_function/insertReminder",formData); alert("Reminder added successfully!"); window.location.reload(); } catch (error) { console.error("Error submitting form:", error); } }; return ( &lt;div className="app-container"&gt; &lt;div className="Header"&gt; &lt;TopBar /&gt; &lt;/div&gt; &lt;div className="form-div"&gt; &lt;Form onSubmit={handleSubmit} /&gt; &lt;/div&gt; &lt;/div&gt; ); } export default UserProfile; 以下に示すように、birthdaygreetings/ディレクトリにあるclient-package.jsonファイルを更新します。 { "name": "birthdaygreetings", "version": "0.0.1", "homepage": "index.html", "login_redirect": "index.html" } クライアントディレクトリの設定が完了しました。 アプリケーションのビジネスロジックを確認しましょう。 - **認証**: - アプリケーションの最初のページはログインページです。ログインページに加えて、ユーザーには新しいアカウントの作成や既存のアカウントのパスワードリセットのオプションが提供されます。 - ユーザーは、Custom User Validation関数にコーディングされた条件を満たした場合にのみアカウントを作成できます。 - アカウントの登録に成功すると、メールに送信される確認リンクを使用してアカウントを確認する必要があります。 - アカウントを確認し、有効なパスワードを作成すると、アプリにログインしてアプリケーションの使用を開始できます。 - **cronのスケジューリング**(リマインダーの作成): - エンドユーザーは、以下の詳細を入力して誕生日グリーティングを作成します: - 名前 - 生年月日 - カスタムメッセージ - 受信者のメールアドレス - これらの詳細が送信されると、Dynamic Cronが作成され、誕生日の日付に毎年ジョブプールに定期的なジョブを送信するようにスケジュールされます。 - **リマインダーの管理**: - 必要なリマインダーを作成した後、アプリケーションでリマインダーの一覧を表示できます。リマインダーの無効化、編集、削除も可能です。 - 無効化アクションはトグルで処理されます。デフォルトでは有効になっています。無効にした場合、または何らかの方法で更新した場合、変更はData Storeに更新され、Dynamic Cronは新しい要件に応じて自動的に調整されます。 - **ログアウト機能**: - 最後に、必要なすべての操作が完了したら、安全にアプリケーションからログアウトできます。 -------------------------------------------------------------------------------- title: "アプリケーションのテスト" description: "Job Schedulingサービスを使用して誕生日グリーティングアプリケーションを構築し、必要な人にメールで自動的に誕生日グリーティングを送信します。" last_updated: "2026-07-02T09:34:22.100Z" source: "https://docs.catalyst.zoho.com/ja/tutorials/birthday-greetings/nodejs/test-app/" service: "All Services" related: - CLIヘルプドキュメント (/ja/cli/v1/cli-command-reference/) - リソースの提供 (/ja/cli/v1/serve-resources/introduction/) -------------------------------------------------------------------------------- # アプリケーションのテスト アプリケーションをリモートコンソールにデプロイする前に、Catalyst CLIを使用してローカルサーバーでアプリケーションをテストし、すべてが正常に動作するか確認できます。 Catalystプロジェクトをローカルで提供するには、プロジェクトディレクトリから以下のコマンドを実行します: catalyst serve <br /> Birthday Greetingsアプリケーションがデフォルトのport 3000で提供されます。コンポーネントのローカルエンドポイントURLが表示されます。 <br /> 情報: クライアントのホームページまたはサブページ、あるいはファンクションにアクセスするたびに、CLIはアクセスされたURLのライブログをHTTPリクエストメソッドとともに表示します。 CLIに表示されるクライアントのローカルURLを使用して、ブラウザでアプリケーションを開くことができます。 <br /> ### サインアップ/サインイン アプリケーションを提供すると、最初に表示されるページはサインインページです。まだアプリケーションにサインアップしていないため、**Sign Up**オプションをクリックします。 <br /> 必要な詳細を入力し、**Sign Up**ボタンをクリックします。 <br /> 注意: Custom User Validation関数にコーディングした認証ロジックを満たす方法でアカウントを作成してください。 メールで送信されたリンクを使用してアカウントを確認する必要があります。メールの内容は、Email Templates機能を使用して設定したものになります。 <br /> リンクをクリックすると、パスワードの作成を求められます。 <br /> アカウントがアプリケーションに登録され、Authenticationコンポーネントの**User Management**セクションで確認できます。また、アカウントにApp Userロールが割り当てられていることも確認できます。 <br /> これでアプリケーションにアクセスできるようになりました。 ### グリーティングの作成 クライアントアプリに必要な詳細を入力し、**Submit**をクリックして必要なグリーティングを作成します。 <br /> このアクションにより、Job SchedulingサービスにDynamic Cronが作成されます。Job Schedulingサービスにあるcronコンポーネントに移動して、cronの詳細を確認します。 <br /> cronをクリックして詳細を確認します。 <br /> これらの詳細から、cronが誕生日の日付の午前12時にジョブプールにジョブを送信するようにスケジュールされていることがわかります。ただし、テスト目的では、**Submit Job**ボタンをクリックしてジョブプールに即座にジョブを送信できます。 <br /> **Execution History**ボタンをクリックして、ジョブのステータスを確認できます。 <br /> 情報: 失敗ステータスが発生した場合は、View Logsボタンをクリックしてください。これにより、Catalyst DevOpsサービスにあるLogsコンポーネントに移動し、デバッグプロセスに役立つ詳細なログを確認できます。 受信者として設定したメールを確認すると、Mailコンポーネントを使用して設定した送信者メールアドレスから誕生日の祝福を受信しているはずです。 <br /> ### リマインダーの管理 **Greetings List**ボタンをクリックして、設定したリマインダーの一覧を表示できます。 <br /> **Edit**ボタンをクリックしてリマインダーを編集できます。 <br /> **Update**をクリックして必要な変更を適用します。 トグルをクリックしてcronを無効にし、祝福の送信を停止できます。また、**Delete**ボタンをクリックしてグリーティングを削除することもできます。 <br /> アプリケーションで行われたすべての変更はData Storeに反映され、cronはそれに応じて調整されます。 アプリケーションは意図した通りに動作するようになりました。 -------------------------------------------------------------------------------- title: "アプリケーションのデプロイ" description: "Job Schedulingサービスを使用して誕生日グリーティングアプリケーションを構築し、必要な人にメールで自動的に誕生日グリーティングを送信します。" last_updated: "2026-07-02T09:34:22.100Z" source: "https://docs.catalyst.zoho.com/ja/tutorials/birthday-greetings/nodejs/deploy-app/" service: "All Services" related: - CLIヘルプドキュメント (/ja/cli/v1/cli-command-reference/) - リソースのデプロイ (/ja/cli/v1/deploy-resources/introduction/) -------------------------------------------------------------------------------- # アプリケーションのデプロイ アプリケーションをリモートコンソールにデプロイできます。 CLIからCatalystプロジェクトをデプロイするには、プロジェクトディレクトリからターミナルで以下のコマンドを実行します: catalyst deploy 最初にファンクションがデプロイされ、次にクライアントコンポーネントがデプロイされます。コンポーネントのアプリURLが表示されます。 <br /> アプリケーションがCatalystコンソールにデプロイされました。 次に、*Cloud Scale*セクションにある*Web Client Hosting*コンポーネントに移動すると、アプリケーションの呼び出しURLを取得できます。 <br /> ブラウザで呼び出しURLを使用して、デプロイされたアプリケーションにアクセスできます。Birthday Greetingsアプリケーションは、そのWebアプリURLからアクセスできるようになりました。 <br />