SoftwareBrothers / adminjs

AdminJS is an admin panel for apps written in node.js
https://adminjs.co
MIT License
8.27k stars 670 forks source link

date and time format to Jalaali #500

Closed everythinginjs closed 4 years ago

everythinginjs commented 4 years ago

Hello dear Wojtek Krysiak,

thanks for your time and efforts on supporting amazing AdminBro panel.

I am trying to convert the gregorian timestamps to Jalaali(persian) is it reachable trough admin-Bro ?

I will be immensely grateful for your help.

wojtek-krysiak commented 4 years ago

you will need to write your own component for edit date. Added this task to the backlog though - maybe we will be able to handle this case in settings

everythinginjs commented 4 years ago

I have tried a couple of times, writing a component for edit date but no luck. I used the following libraries https://mberneti.github.io/react-datepicker2/ https://kiarash-z.github.io/react-modern-calendar-datepicker/ https://kiarash-z.github.io/react-modern-calendar-datepicker/docs/typescript

but it does't work nor give an error back. if you can give me an example I will be appreciated. Otherwise, I should wait for handling this case in settings.

thanks a bunch,

wojtek-krysiak commented 4 years ago

take a look at this: https://github.com/SoftwareBrothers/admin-bro/blob/master/src/frontend/components/property-type/datetime/edit.tsx

everythinginjs commented 4 years ago

Thanks dear Krysiak for your replay,

the following package has got both Gregorian and Jalali calender which I can easily call locale='fa' for jalali one https://kiarash-z.github.io/react-modern-calendar-datepicker/docs/typescript

it works well in front-end but I can not handle onChange for affecting the database.Since I am not familiar to reactjs any tips that can help me to get through of this problem would be appreciated.

import React, { memo } from 'react';
import { EditPropertyProps, Label, FormGroup, DatePickerProps } from 'admin-bro';
import { recordPropertyIsEqual } from '../record-property-is-equal';
import '../src/DatePicker.js';
import DatePicker, { Day, DayValue } from 'react-modern-calendar-datepicker';

const Edit: React.FC<EditPropertyProps> = (props) => {
  const { property, onChange, record } = props
  const error = record.errors && record.errors[property.name]
  const [day, setDay] = React.useState<DayValue>(null);

  return (
    <FormGroup error={!!error}>
      <Label htmlFor={property.name}>{property.label}</Label>
      <DatePicker
        value={day}
        onChange={setDay} // how should be handled for saving to DB ?
        locale='fa'
      />
    </FormGroup>

  )
}

export default memo(Edit, recordPropertyIsEqual)

thankful for your assistance,

cloudratha commented 4 years ago

You are extracting the onChange prop, but are not using it. Check out handleChange in frontend/hooks/use-record.tsx for usage.

everythinginjs commented 4 years ago

thanks, @cloudratha yes I know because I couldn't handle that part.

the following is the original way of handling onChange from (https://github.com/SoftwareBrothers/admin-bro/blob/master/src/frontend/components/property-type/datetime/edit.tsx)

onChange={(data: string): void => onChange(property.name, data)}

I did the same thing but it didn't work

cloudratha commented 4 years ago

You are creating local state for the day, rather than extracting it from the record params. The onChange prop updates the original base record. So it makes sense to extract the value from the record, otherwise your changes aren't going to reflect.

Something like the below.

const day = record.params[property.name];
everythinginjs commented 4 years ago

thanks for your kindness @cloudratha I'll give it another try hope it works this time.

everythinginjs commented 4 years ago

Okay, I made some changes to the code;

import React from 'react';

import { EditPropertyProps } from 'admin-bro';
import '../src/DatePicker.js';
import DatePicker, { DayValue } from 'react-modern-calendar-datepicker';

const Edit: React.FC<EditPropertyProps> = (props) => {
  const { onChange, record, property } = props

  const day = (record.params && record.params[property.name]) || ''

  return (
    <>
      <DatePicker value={day} onChange={(data: string): void => onChange(property.name, data)} locale='fa' />
    </>
  );
}

export default Edit

when I print onChange into console (onChange= (date) => console.log(date)) I get jalaali date which means it works but when I use this one for handling onChange

(data: string): void => onChange(property.name, data)

I get this error on onChange attribute I think the problem comes from string type since I change it to something else the error gone. but it is not saved into DB.

Screen Shot 1399-04-18 at 17 35 45
cloudratha commented 4 years ago

I'm not familiar with react-modern-calendar-datepicker, but looking at the error, you need to format the data into a string format that should reflect in the DB.

This is the onChange handler in AdminBro for DatePicker: https://github.com/SoftwareBrothers/admin-bro/blob/c03cc997312b0849085f3e45a6efe6ffc58d94ee/src/frontend/components/design-system/molecules/date-picker.tsx#L164-L168

And the format method: https://github.com/SoftwareBrothers/admin-bro/blob/c03cc997312b0849085f3e45a6efe6ffc58d94ee/src/frontend/components/design-system/molecules/date-picker.tsx#L129-L130

everythinginjs commented 4 years ago

thank you @cloudratha for your time, the reason of my late reply was trying your solution out which I think you are right.

I did the things that you said but I still get the same error from Onchange property which says date is not string.

import React, { memo } from 'react';
import { EditPropertyProps, FormGroup, Label, FormMessage } from 'admin-bro';
import { recordPropertyIsEqual } from '../record-property-is-equal'
import DatePicker from 'react-modern-calendar-datepicker'
import '../src/react-datepicker.js'
export type DatePickerProps = {
  /**
   * If datepicker should be disabled
   */
  disabled?: boolean;
  /**
   * selected date
   */
  value?: string | Date;
  /**
   * on change callback taking string as a date
   */
  onChange: (date: string) => void;
}

const pad = (n: number): string => (n < 10 ? `0${n.toString()}` : n.toString())

const format = (date: Date): string => `${date.getFullYear()}-${pad(date.getMonth() + 1)
  }-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`

const DatePickerFarsi: React.FC<DatePickerProps & EditPropertyProps> = (props) => {
  const { value, onChange, disabled, ...other } = props
  const { property, record } = props
  const day = (record.params && record.params[property.name]) || ''
  const error = record.errors && record.errors[property.name]

  let dateValue: Date | undefined
  let stringValue: string | undefined = day && day.toString()

  if (day && day.constructor.name !== 'Date') {
    const dateNum = Date.parse(day as string) || undefined
    if (dateNum) {
      dateValue = new Date(dateNum)
    }
  } else if (day && day.constructor.name === 'Date') {
    stringValue = format(day as Date)
  }

  const onDatePickerChange = (date: Date): void => {
    if (!disabled) {
      onChange(format(date))
    }
  }

  return (
    <FormGroup error={!!error}>
      <Label htmlFor={property.name}>{property.label}</Label>
      <DatePicker
        value={day}
        disabled={disabled}
        onChange={onDatePickerChange}
        locale='fa'
        inline {...other}
      />
      <FormMessage>{error && error.message}</FormMessage>
    </FormGroup>
  )
}

export default memo(DatePickerFarsi, recordPropertyIsEqual)

thanks for assistance,

cloudratha commented 4 years ago

Not sure what the year ranges for an fa calendar are and how they get converted for the DB, but make sure its correct in the format and dateStringToDay methods.

NB: this is just for a Date field, not sure if the calendar supports time. You might want to add back the time options to the format method with sensible defaults in the correct locale.

Also the calendar doesnt support the disabled prop, so maybe handle that yourself.

import React from "react";
import { EditPropertyProps, FormGroup, Label, FormMessage } from "admin-bro";
import DatePicker, { Day, utils } from "react-modern-calendar-datepicker";

export type DatePickerProps = {
  /**
   * If datepicker should be disabled
   */
  disabled?: boolean;
  /**
   * selected date
   */
  value?: string | Date;
  /**
   * on change callback taking string as a date
   */
  onChange: (date: string) => void;
};

const pad = (n: number): string => (n < 10 ? `0${n.toString()}` : n.toString());

const format = (date: Day): string =>
  `${date.year}-${pad(date.month)}-${pad(date.day)}`;

const dateStringToDay = (date: string): Day => {
  const [year, month, day] = date.split("-").map((value) => Number(value));
  return { day, month, year };
};

const DatePickerFarsi: React.FC<DatePickerProps & EditPropertyProps> = (
  props
) => {
  const { value, onChange, property, record, ...other } = props;
  const dayValue = record.params && record.params[property.name];
  const day = dayValue ? dateStringToDay(dayValue) : utils("fa").getToday();

  const error = record.errors && record.errors[property.name];
  const onDatePickerChange = (day: Day): void => {
    onChange(property.name, format(day));
  };

  return (
    <FormGroup error={!!error}>
      <Label htmlFor={property.name}>{property.label}</Label>
      <DatePicker
        value={day}
        onChange={onDatePickerChange}
        locale="fa"
        {...other}
      />
      <FormMessage>{error && error.message}</FormMessage>
    </FormGroup>
  );
};

export default DatePickerFarsi;
everythinginjs commented 4 years ago

Thank you so much @cloudratha for everything, you saved me. your approach works perfect and I can see it saves to the database love you.

Screen Shot 1399-04-19 at 14 17 44
everythinginjs commented 4 years ago

I feel like to close this issue since @cloudratha could answer that. Thanks AdminBro.