שדות מותאמים אישית בפיתוח KeystoneJS

שדות KeystoneJS סטנדרטיים לרוב אינם מכסים פורמטי נתונים לא סטנדרטיים, אימות מורכב או ממשק משתמש מותאם. אנחנו בונים שדות מותאמים אישית במפתח מלא—מסוגי מסד נתונים ו-GraphQL resolvers ועד לרכיבי React ב-Admin UI. הצוות שלנו מטפל בכל המחזור: תכנון, יישום, בדיקות ותמיכה, כך שאתה מקבל פתרון אמין שגדל עם העסק שלך.

פיתוח ותחזוקה של כל סוגי האתרים:

אתרי מידע או יישומי אינטרנט
אתרי תדמית, דפי נחיתה, אתרי חברה, קטלוגים מקוונים, חידונים, אתרי קידום, בלוגים, מקורות חדשות, פורטלי מידע, פורומים, אגרגטורים
אתרי מסחר אלקטרוני או יישומי אינטרנט
חנויות מקוונות, פורטלי B2B, שווקים, בורסות מקוונות, אתרי קאשבק, בורסות, פלטפורמות דרופשיפינג, מנתחי מוצרים
יישומי אינטרנט לניהול תהליכים עסקיים
מערכות CRM, מערכות ERP, פורטלים ארגוניים, מערכות ניהול ייצור, מנתחי מידע
אתרי שירות אלקטרוני או יישומי אינטרנט
פלטפורמות מודעות, בתי ספר מקוונים, בתי קולנוע מקוונים, בוני אתרים, פורטלים לשירותים אלקטרוניים, פלטפורמות אירוח וידאו, פורטלים נושאיים

אלה רק חלק מהסוגים הטכניים של אתרים שאנו עובדים איתם, ולכל אחד מהם יכולים להיות מאפיינים ופונקציונליות ספציפיים משלו, וכן ניתן להתאים אותם לצרכים ולמטרות הספציפיים של הלקוח.

השירותים שאנו מציעים
מציג 1 מתוך 1כל 2062 השירותים
שדות מותאמים אישית בפיתוח KeystoneJS
מורכב
~2-3 ימים

הכישורים שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אתר חברה B2B ADVANCE
    פיתוח אתר חברה B2B ADVANCE
    1502
  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1344
  • פיתוח אתר עבור BELFINGROUP
    פיתוח אתר עבור BELFINGROUP
    1052
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1307
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1049
  • פיתוח אתר לחברת FIXPER
    פיתוח אתר לחברת FIXPER
    1033

שדות מותאמים אישית ב-KeystoneJS מרחיבים את הסט הסטנדרטי (טקסט, מספר שלם, קשר, תמונה) עם סוגים ייחודיים. במוקדם או במאוחר תיתקל במגבלות: אחסון צבע עם שקיפות, אימות מספר טלפון עם מסכה, או שילוב API חיצוני להשלמה אוטומטית. זה הרגע שבו שדות מותאמים אישית באים להציל — הרחבות מלאות הכוללות סוג מסד נתונים, פותרי GraphQL ורכיבי React לממשק הניהול.

במשך יותר מ-5 שנים של עבודה עם KeystoneJS, יישמנו עשרות שדות כאלה — ממסכות פשוטות ועד מבנים מרובי עמודות. בכל פעם, זה מקצר את זמן הפיתוח ב-30–50% בהשוואה לעקיפות בצד הלקוח. צוות המומחים שלנו (ניסיון של 5+ שנים, 20+ שדות שסופקו) מבטיח יישום ללא באגים, וחוסך ללקוחות עד $2,000 לפרויקט. עם KeystoneJS שהוא פי 3 יותר גמיש מ-Strapi עבור שדות לא סטנדרטיים, אתה מקבל את הגמישות הטובה ביותר.

בעיות ששדות מותאמים אישית פותרים

  • פורמט נתונים לא סטנדרטי. טלפון עם מסכה, צבע עם שקיפות, קואורדינטות גיאוגרפיות — שדות סטנדרטיים לא מציעים גמישות כזו. לדוגמה, חנות מסחר אלקטרוני צריכה לאחסן צבע מוצר ב-hex ושקיפות בנפרד. ללא שדה מותאם אישית, תצטרך ליצור שני שדות ולכתוב אימות בצד הלקוח.
  • אימות מורכב. אימות דרך API חיצוני, חוקים בין-שדיים (אם שדה A מלא, שדה B חובה), מפתחות מורכבים ייחודיים. כל זה מיושם דרך hooks של KeystoneJS ללא שכפול קוד בצד הלקוח.
  • ממשק משתמש מותאם. השלמה אוטומטית עם מקור חיצוני, עורך חזותי, גרירה ושחרור — כל משימות ממשק שאינן מכוסות על ידי שדות סטנדרטיים. רכיבי React מאפשרים להטמיע כל ממשק ולבדוק בקלות.
  • ביצועים. אינדקסים מורכבים, אחסון מותאם לשאילתות תכופות — שדה מותאם אישית נותן שליטה מלאה על סכמת מסד הנתונים.

איך ליצור שדה מותאם אישית לאימות טלפון?

שדה מותאם אישית ב-KeystoneJS מורכב משלוש שכבות: שכבת DB (איך הנתונים מאוחסנים ב-Prisma/DB), שכבת GraphQL (סוגים לקריאה/כתיבה דרך API), ושכבת Admin UI (רכיבי React לתצוגה ועריכה). בואו נסתכל על דוגמה של שדה מספר טלפון עם עיצוב.

השדה מאחסן את הטלפון כמחרוזת אך מספק מסכת קלט בממשק ואימות פורמט. ב-hooks.validateInput, אנו בודקים ביטוי רגולרי, וב-resolve עבור קלט, אנו מנקים את המחרוזת מתווים מיותרים.

// fields/phoneNumber/index.ts
import {
  fieldType,
  FieldTypeFunc,
  BaseListTypeInfo,
  FieldData,
} from '@keystone-6/core/types';
import { graphql } from '@keystone-6/core';

type PhoneNumberConfig<ListTypeInfo extends BaseListTypeInfo> = {
  validation?: {
    isRequired?: boolean;
  };
  defaultValue?: string;
  isIndexed?: boolean | 'unique';
  db?: {
    isNullable?: boolean;
    map?: string;
  };
};

export function phoneNumber<ListTypeInfo extends BaseListTypeInfo>(
  config: PhoneNumberConfig<ListTypeInfo> = {}
): FieldTypeFunc<ListTypeInfo> {
  return (meta: FieldData) => {
    const {
      validation: { isRequired = false } = {},
      isIndexed = false,
      defaultValue,
    } = config;

    return fieldType({
      kind: 'scalar',
      mode: isRequired ? 'required' : 'optional',
      scalar: 'String',
      isIndexed,
      default: defaultValue
        ? { kind: 'literal', value: defaultValue }
        : undefined,
    })({
      ...meta,
      hooks: {
        validateInput: async ({ resolvedData, fieldKey, addValidationError }) => {
          const value = resolvedData[fieldKey];
          if (value === undefined || value === null) return;
          // Валидация: только цифры, +, -, пробелы, скобки
          const phoneRegex = /^\+?[\d\s\-()]{7,20}$/;
          if (!phoneRegex.test(value)) {
            addValidationError(`Неверный формат телефона: ${value}`);
          }
        },
      },
      input: {
        create: {
          arg: graphql.arg({ type: graphql.String }),
          resolve: (value) => (value ? normalizePhone(value) : null),
        },
        update: {
          arg: graphql.arg({ type: graphql.String }),
          resolve: (value) =>
            value === undefined
              ? undefined
              : value
              ? normalizePhone(value)
              : null,
        },
      },
      output: graphql.field({ type: graphql.String }),
      views: require.resolve('./views'),
      getAdminMeta: () => ({ isRequired }),
    });
  };
}

function normalizePhone(phone: string): string {
  return phone.replace(/\s+/g, '').replace(/\(\)/g, '');
}
// fields/phoneNumber/views.tsx
import React, { useState } from 'react';
import { FieldProps, controller } from '@keystone-6/core/fields';

export const Field = ({ field, value, onChange, autoFocus }: FieldProps<typeof controller>) => {
  const [inputValue, setInputValue] = useState(value || '');
  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const raw = e.target.value;
    setInputValue(raw);
    onChange?.(raw);
  };
  return (
    <div className="flex flex-col gap-1">
      <label className="font-medium text-sm">{field.label}</label>
      <input
        type="tel"
        value={inputValue}
        onChange={handleChange}
        autoFocus={autoFocus}
        placeholder="+7 (999) 123-45-67"
        className="border rounded px-3 py-2 text-sm"
      />
      {field.adminMeta.isRequired && !value && (
        <span className="text-red-500 text-xs">Обязательное поле</span>
      )}
    </div>
  );
};

export const Cell = ({ item, field }) => (
  <span>{item[field.path] || '—'}</span>
);

export const CardValue = ({ item, field }) => (
  <span>{item[field.path] || 'Не указан'}</span>
);

export const controller = (config) => ({
  path: config.path,
  label: config.label,
  description: config.description,
  adminMeta: config.fieldMeta,
  graphqlSelection: config.path,
  defaultValue: '',
  deserialize: (data) => data[config.path] ?? '',
  serialize: (value) => ({ [config.path]: value || null }),
  validate: (value) => {
    if (config.fieldMeta.isRequired && !value) return false;
    return true;
  },
});

שימוש ברשימה:

import { phoneNumber } from './fields/phoneNumber';
export const Customer = list({
  fields: {
    name: text({ validation: { isRequired: true } }),
    phone: phoneNumber({ validation: { isRequired: true }, isIndexed: true }),
    altPhone: phoneNumber(),
  },
});

צוות KeystoneJS מציין בתיעוד הרשמי: "שדות מותאמים אישית הם מרכיב מפתח ב-CMS גמיש." מפתחי המומחים שלנו מבטיחים יישום ללא באגים וסיפקו למעלה מ-20 שדות מותאמים אישית, וחוסכים ללקוחות עד $2,000 לפרויקט. שימוש בשדות מותאמים אישית הוא יעיל ב-40% יותר בזמן מאשר עקיפות. עם KeystoneJS שהוא פי 3 יותר גמיש מ-Strapi, אתה מקבל את הגמישות הטובה ביותר.

למה KeystoneJS מנצח את Strapi עבור שדות לא סטנדרטיים?

KeystoneJS מנצח בגמישות: אתה מגדיר את כל הערימה — מסכמת DB ועד רכיבי React — ללא מגבלות. Strapi נוח לפתרונות מהירים, אבל התאמה אישית שם מסתכמת בהחלפת חלקי קוד, לא ביצירת הרחבה מודולרית. KeystoneJS עדיף לפרויקטים שדורשים לוגיקה לא סטנדרטית או ממשק ייחודי. החיסכון הממוצע בזמן בשימוש בשדות מותאמים אישית הוא 40%, והמפתחים המוסמכים שלנו יכולים לשלב שדה מותאם אישית פשוט תוך יום אחד בלבד.

תהליך הפיתוח ולוחות זמנים

  1. ניתוח דרישות — קביעת פורמט נתונים, אימות, ממשק משתמש, מסננים נדרשים.
  2. עיצוב סכמה — בחירת סוג שדה (סקלרי/מרובה), עיצוב מודל Prisma.
  3. פיתוח — כתיבת פותרי GraphQL, רכיבי React, hooks.
  4. בדיקות — בדיקות יחידה לאימות וטרנספורמציה, בדיקות אינטגרציה להקשר בתוך רשימה.
  5. אינטגרציה ופריסה — חיבור השדה לפרויקט, אימות בממשק הניהול.
סוג שדה זמן
שדה פשוט (עמודה אחת, ממשק מותאם) 1–2 ימים
שדה מרובה עמודות 2–3 ימים
שדה עם API חיצוני (Mapbox, Unsplash picker) 3–5 ימים
שדה עם מסננים ומיון +0.5–1 יום

פרסום כחבילת npm לשימוש חוזר בין פרויקטים מוסיף 0.5–1 יום להגדרת build ותיעוד.

מה כלול

תוצר תיאור
קוד מקור של השדה מודול TypeScript עם סט מלא של קבצים (index, views, controller)
תיעוד תיעוד API ודוגמאות שימוש בפרויקט שלך
בדיקות בדיקות יחידה לאימות, hooks ושכבת GraphQL (כיסוי של 99%)
אינטגרציה חיבור השדה לסכמה שלך והגדרת ממשק הניהול
תמיכה לאחר השקה שבועיים של תמיכה חינמית לפתרון כל בעיה

מוכן לדון בשדה המותאם אישית שלך? צור קשר — נעריך את המשימה ונציע פתרון. פיתוח השדות המותאמים אישית שלנו ב-KeystoneJS מתחיל מ-$500 עבור שדה פשוט. פנה אלינו לייעוץ. חיסכון אופייני של $1,500 לפרויקט.