react-redux#shallowEqual JavaScript Examples

The following examples show how to use react-redux#shallowEqual. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: index.jsx    From react-firebase-admin with MIT License 6 votes vote down vote up
DatePickerStyled = ({ date, onChange }) => {
  const onDateChangedHandler = (value) =>
    onChange(value ? value.toDateString() : new Date().toDateString());

  const { locale } = useSelector(
    (state) => ({
      locale: state.preferences.locale,
    }),
    shallowEqual
  );

  return (
    <DatePicker
      locale={locale}
      dateFormat={dateFormat(locale)}
      selected={date}
      onChange={onDateChangedHandler}
    />
  );
}
Example #2
Source File: index.js    From rtk-demo with MIT License 6 votes vote down vote up
export function useHackerNewsArticles({ limit = 20, offset = 0 } = {}) {
  useInjectReducer({ key: name, reducer });
  useInjectSaga({ key: name, saga });

  const dispatch = useDispatch();
  const store = useSelector(makeSelectHackerNewsArticles(), shallowEqual);

  useEffect(() => {
    if (!store?.data?.length && !store?.loading) {
      dispatch(
        actions.fetch({
          offset,
          limit,
        }),
      );
    }
  }, []);

  return store;
}
Example #3
Source File: DocsIndexContainer.js    From signdocs with MIT License 6 votes vote down vote up
DocsIndexContainer = () => {
  const [loading, setLoading] = useState(true);
  const dispatch = useDispatch();

  const docs = useSelector(getAllDocuments, isEqual);

  useEffect(() => {
    (async function getAllDocs() {
      setLoading(true);
      dispatch(fetchDocuments()).done(() => setLoading(false));
    })();
  }, []);

  const docsArray = docs && Object.keys(docs) ? Object.values(docs) : [];

  const currentUser = useSelector(getCurrentUser, shallowEqual);

  const DocsIndexOrNoDocs = () =>
    docsArray.length > 0 ? (
      <DocsIndex docs={docsArray} currentUser={currentUser} />
    ) : (
      <NoDocsCallToCreate />
    );

  return (
    <div id="index-container">
      <Sidebar />
      <div className="index-inbox">
        <div className="search-bar">
          <h2>Inbox</h2>
        </div>
        {loading ? <div>Loading...</div> : <DocsIndexOrNoDocs />}
      </div>
    </div>
  );
}
Example #4
Source File: secretaries-tabs.js    From what-front with MIT License 6 votes vote down vote up
SecretariesTabs = ({ index }) => {
  const { id } = useParams();

  const [loadAllSecretaries] = useActions([fetchSecretaries]);
  const { currentUser } = useSelector(currentUserSelector, shallowEqual);

  useEffect(() => {
    loadAllSecretaries();
  }, [loadAllSecretaries]);

  if (currentUser.role === 4) {
    return (
      <Tabs defaultIndex={index} className="container col-lg-6 col-md-8 col-sm-12 pt-5" linkBack={paths.SECRETARIES}>
        <Tab title="Secretary's details" index={0}>
          <SecretarysDetails id={Number(id)} />
        </Tab>
        <Tab title="Edit secretary">
          <EditSecretarysDetails id={Number(id)} />
        </Tab>
      </Tabs>
    );
  }
  return <SecretarysDetails id={Number(id)} />;
}
Example #5
Source File: AvailableImagesTile.js    From edge-frontend with Apache License 2.0 6 votes vote down vote up
AvailableImageTile = ({ onNewImageClick }) => {
  const { isLoading, hasError, data } = useSelector(
    ({ imagesReducer }) => ({
      isLoading:
        imagesReducer?.isLoading !== undefined
          ? imagesReducer?.isLoading
          : true,
      hasError: imagesReducer?.hasError || false,
      data: imagesReducer?.data || null,
    }),
    shallowEqual
  );

  return (
    <AvailableImageTileBase>
      <CardBody>
        {isLoading ? (
          <Bullseye>
            <Spinner />
          </Bullseye>
        ) : hasError ? (
          data
        ) : (
          <Button variant="link" style={{ paddingLeft: 0 }}>
            {data.meta.count} images
          </Button>
        )}
      </CardBody>
      <CardFooter>
        <Button variant="primary" onClick={() => onNewImageClick()}>
          Create new image
        </Button>
      </CardFooter>
    </AvailableImageTileBase>
  );
}
Example #6
Source File: index.jsx    From react-firebase-admin with MIT License 6 votes vote down vote up
NotFound = () => {
  const location = useLocation();

  const { isAuth } = useSelector(
    state => ({
      isAuth: !!state.auth.userData.id
    }),
    shallowEqual
  );

  const userPath = isAuth ? path.ROOT : path.LOGIN;

  return (
    <section className="hero is-fullheight">
      <div className="hero-body">
        <section className={`section ${classes.section}`}>
          <div className="container">
            <div className="columns is-vcentered is-desktop">
              <div className="column has-text-centered">
                <h1 className="title">{useFormatMessage('NotFound.404')}</h1>
                <p className="subtitle">
                  {useFormatMessage('NotFound.url', { url: location.pathname })}
                </p>
                <Link className="button is-info is-normal" to={userPath}>
                  {useFormatMessage('NotFound.back')}
                </Link>
              </div>
              <div className="column has-text-centered">
                <img src={NotFoudImage} alt="404 error" />
              </div>
            </div>
          </div>
        </section>
      </div>
    </section>
  );
}
Example #7
Source File: hooks.js    From frontend-app-course-authoring with GNU Affero General Public License v3.0 5 votes vote down vote up
export function useModel(type, id) {
  return useSelector(
    state => (state.models[type] !== undefined ? state.models[type][id] : undefined),
    shallowEqual,
  );
}
Example #8
Source File: secretarys-details.js    From what-front with MIT License 5 votes vote down vote up
SecretarysDetails = ({ id }) => {
  const { data, isLoading, isLoaded } = useSelector(secretariesSelector, shallowEqual);
  const { currentUser } = useSelector(currentUserSelector, shallowEqual);
  const history = useHistory();
  const secretary = data.find((user) => user.id === id);

  useEffect(() => {
    if (!secretary && isLoaded) {
      history.push(paths.NOT_FOUND);
    }
  }, [secretary, isLoaded, history]);

  return (
    <div className="container">
      <div className="row justify-content-center">
        <div className={classNames('col-sm-12 card shadow',
          { 'col-md-12': currentUser.role === 4, 'col-md-6': currentUser.role === 8 })}
        >
          <div className="px-2 py-4">
            <h3>Secretary&apos;s details</h3>
            <hr />
            <WithLoading isLoading={isLoading && !isLoaded} className="d-block mx-auto">
              <div className="row">
                <div className="col-12 col-md-6 font-weight-bolder">First Name:</div>
                <div className="col-12 col-md-6">{secretary?.firstName}</div>
              </div>
              <hr />
              <div className="row">
                <div className="col-12 col-md-6 font-weight-bolder">Last Name:</div>
                <div className="col-12 col-md-6">{secretary?.lastName}</div>
              </div>
              <hr />
              <div className="row mb-3">
                <div className="col-12 col-md-6 font-weight-bolder">Email address:</div>
                <div className="col-12 col-md-6">{secretary?.email}</div>
              </div>
            </WithLoading>
          </div>
        </div>
      </div>
    </div>
  );
}
Example #9
Source File: index.jsx    From react-firebase-admin with MIT License 5 votes vote down vote up
Profile = () => {
  const { userData } = useSelector(
    (state) => ({
      userData: state.auth.userData,
    }),
    shallowEqual
  );

  const dispatch = useDispatch();

  const onSubmitHandler = (value) => {
    const newUser = {
      ...value,
      file: value?.file[0] || null,
      isEditing: true,
      isProfile: true,
      id: userData.id,
    };
    dispatch(modifyUser(newUser));
  };

  return (
    <>
      <section className="hero is-hero-bar">
        <div className="hero-body">
          <h1 className="title">{useFormatMessage('Profile.profile')}</h1>
        </div>
      </section>
      <section className="section is-main-section">
        <UserForm
          isEditing
          isProfile
          user={userData}
          onSubmitHandler={onSubmitHandler}
          schema={schema}
        />
        <ChangePassword />
      </section>
    </>
  );
}
Example #10
Source File: courses-tabs.js    From what-front with MIT License 5 votes vote down vote up
CoursesTabs = ({ index }) => {
  const page = useLocation();
  const { id } = useParams();
  const { currentUser } = useSelector(currentUserSelector, shallowEqual);
  const loadActiveCourses = useActions(fetchActiveCourses);
  const coursesActiveData = useSelector(coursesActiveSelector, shallowEqual);
  const loadNotActiveCourses = useActions(fetchNotActiveCourses);
  const coursesNotActiveData = useSelector(coursesNotActiveSelector, shallowEqual);

  const isCourseEnable = coursesActiveData.data.map(({ id }) => id).includes(Number(id));
  
  useEffect(() => {
    loadActiveCourses();
  }, [loadActiveCourses]);

  useEffect(() => {
    loadNotActiveCourses();
  }, [loadNotActiveCourses]);

  if (currentUser.role === 8 || currentUser.role === 4) {
    if(isCourseEnable){
      return (
        <Tabs defaultIndex={index} className="container w-50 pt-5" linkBack={paths.COURSES}>
          <Tab title="Course details">
            <CourseDetails
              id={Number(id)}
              coursesData={coursesActiveData}
            />
          </Tab>
          <Tab title="Edit course details">
            <EditCourse
              id={Number(id)}
              coursesData={coursesActiveData}
            />
          </Tab>
        </Tabs>
      );
    }
    else{
      return (
        <Tabs defaultIndex={index} className="container w-50 pt-5" linkBack={paths.COURSES}>
          <Tab title="Course details">
            <CourseDetails
              id={Number(id)}
              coursesData={coursesNotActiveData}
            />
          </Tab>
          <Tab title="Edit course details">
            <EditCourse
              id={Number(id)}
              coursesData={coursesNotActiveData}
            />
          </Tab>
        </Tabs>
      );
    }
  } else {
    if(isCourseEnable){
      return <CourseDetails id={Number(id)} coursesData={coursesActiveData} />
    }
    else{
      return <CourseDetails id={Number(id)} coursesData={coursesNotActiveData} />
    }    
  }
}
Example #11
Source File: index.jsx    From react-firebase-admin with MIT License 5 votes vote down vote up
Aside = ({ handleMobileToggle }) => {
  const { isAdmin } = useSelector(
    (state) => ({
      isAdmin: state.auth.userData.isAdmin,
    }),
    shallowEqual
  );

  const usersMessage = useFormatMessage('Aside.users');

  return (
    <aside className="aside is-placed-left is-expanded">
      <Link to={paths.ROOT} className="aside-tools">
        <div className="aside-tools-label">
          <span>
            <b>React</b> Firebase
          </span>
        </div>
      </Link>
      <div className="menu is-menu-main">
        <ul className="menu-list">
          <li>
            <NavLink
              to={paths.ROOT}
              className="has-icon"
              onClick={handleMobileToggle}
            >
              <span className="icon">
                <i className="mdi mdi-home" />
              </span>
              <span className="menu-item-label">
                {useFormatMessage('Aside.home')}
              </span>
            </NavLink>
          </li>
          {isAdmin && (
            <li>
              <NavLink
                to={paths.USERS}
                className="has-icon"
                onClick={handleMobileToggle}
              >
                <span className="icon">
                  <i className="mdi mdi-account-supervisor" />
                </span>
                <span className="menu-item-label">{usersMessage}</span>
              </NavLink>
            </li>
          )}
          <SubMenu label={useFormatMessage('Aside.dropdownMenu')}>
            <li>
              <NavLink
                className={classes.submenuLink}
                to={paths.SUBMENU_1}
                onClick={handleMobileToggle}
              >
                <span>{useFormatMessage('Aside.submenu1')}</span>
              </NavLink>
            </li>
            <li>
              <NavLink
                className={classes.submenuLink}
                to={paths.SUBMENU_2}
                onClick={handleMobileToggle}
              >
                <span>{useFormatMessage('Aside.submenu2')}</span>
              </NavLink>
            </li>
          </SubMenu>
        </ul>
      </div>
    </aside>
  );
}
Example #12
Source File: students-tabs.js    From what-front with MIT License 5 votes vote down vote up
StudentsTabs = ({ index }) => {
  const { currentUser } = useSelector(currentUserSelector, shallowEqual);
  const { id } = useParams();

  const [
    fetchStudentById,
    fetchGroups,
    fetchStudentGroups,
    fetchStudentLessons,
  ] = useActions([
    loadStudentById,
    globalLoadStudentGroups,
    loadStudentGroups,
    fetchLessonsByStudentId,
  ]);

  useEffect(() => {
    fetchStudentById(id);
    fetchGroups();
    fetchStudentGroups(id);
    fetchStudentLessons(id);
  }, [
    fetchGroups,
    fetchStudentById,
    fetchStudentGroups,
    fetchStudentLessons,
    id,
  ]);

  const arrow = (
    <svg
      width="1em"
      height="1em"
      viewBox="0 0 16 16"
      className="bi bi-arrow-left"
      fill="currentColor"
      xmlns="http://www.w3.org/2000/svg"
    >
      <path
        fillRule="evenodd"
        d="M15 8a.5.5 0 0 0-.5-.5H2.707l3.147-3.146a.5.5 0 1 0-.708-.708l-4 4a.5.5 0 0 0 0 .708l4 4a.5.5 0 0 0 .708-.708L2.707 8.5H14.5A.5.5 0 0 0 15 8z"
      />
    </svg>
  );

  return (
    <>
      {currentUser.role === 2 ? (
        <section
          defaultIndex={index}
          className="container w-50 pt-5"
          linkBack="/students"
        >
          <Link
            className="nav-item nav-link d-flex align-items-center"
            to={{
              pathname: '/students',
            }}
          >
            {arrow}
          </Link>
          <StudentDetails />
        </section>
      ) : (
        <Tabs
          defaultIndex={index}
          className="container w-50 pt-5"
          linkBack="/students"
        >
          <Tab title="Student details">
            <StudentDetails />
          </Tab>
          <Tab title="Edit student details">
            <EditStudentsDetails id={id} />
          </Tab>
        </Tabs>
      )}
    </>
  );
}
Example #13
Source File: LogsTable.js    From sed-frontend with Apache License 2.0 5 votes vote down vote up
LogsTable = () => {
  const [opened, setOpened] = useState([]);
  const dispatch = useDispatch();
  const logLoaded = useSelector(
    ({ logReducer }) => logReducer?.loaded || false
  );
  const rows = useSelector(({ logReducer }) => logReducer?.results || []);
  const pagination = useSelector(
    ({ logReducer }) => ({
      itemCount: logReducer?.total,
      perPage: logReducer?.limit,
      page:
        Math.floor((logReducer?.offset || 0) / (logReducer?.limit || 0)) + 1,
    }),
    shallowEqual
  );
  useEffect(() => {
    dispatch(fetchLog());
  }, []);
  const onCollapse = (_e, _key, isOpen, { id }) => {
    setOpened(() =>
      isOpen ? [...opened, id] : opened.filter((openId) => openId !== id)
    );
  };

  const setPage = useCallback(
    (_e, pageNumber) =>
      dispatch(fetchLog({ page: pageNumber, perPage: pagination.perPage })),
    [dispatch, pagination.perPage]
  );

  const setPerPage = useCallback(
    (_e, perPage) => dispatch(fetchLog({ page: 1, perPage })),
    [dispatch]
  );

  return (
    <Fragment>
      <PrimaryToolbar
        pagination={
          logLoaded ? (
            {
              ...pagination,
              onSetPage: setPage,
              onPerPageSelect: setPerPage,
            }
          ) : (
            <Skeleton width="33%" />
          )
        }
      />
      {logLoaded ? (
        <Table
          aria-label="Logs table"
          variant={TableVariant.compact}
          rows={rowsMapper(rows, opened)}
          cells={columns}
          onCollapse={onCollapse}
        >
          <TableHeader />
          <TableBody />
        </Table>
      ) : (
        <SkeletonTable colSize={3} rowSize={10} />
      )}
      <TableToolbar isFooter>
        {logLoaded ? (
          <Pagination
            {...pagination}
            variant={PaginationVariant.bottom}
            onSetPage={setPage}
            onPerPageSelect={setPerPage}
          />
        ) : (
          <Skeleton width="33%" />
        )}
      </TableToolbar>
    </Fragment>
  );
}
Example #14
Source File: hooks.js    From frontend-app-course-authoring with GNU Affero General Public License v3.0 5 votes vote down vote up
export function useModels(type, ids) {
  return useSelector(
    state => ids.map(
      id => (state.models[type] !== undefined ? state.models[type][id] : undefined),
    ),
    shallowEqual,
  );
}
Example #15
Source File: DeviceSummaryTile.js    From edge-frontend with Apache License 2.0 5 votes vote down vote up
DeviceSummaryTile = () => {
  const { isLoading, hasError, data } = useSelector(
    ({ deviceSummaryReducer }) => ({
      isLoading:
        deviceSummaryReducer?.isLoading !== undefined
          ? deviceSummaryReducer?.isLoading
          : true,
      hasError: deviceSummaryReducer?.hasError || false,
      data: deviceSummaryReducer?.data || null,
    }),
    shallowEqual
  );

  if (isLoading) {
    return (
      <Card className="tiles-card">
        <CardTitle>Device summary information</CardTitle>
        <CardBody>
          <Bullseye>
            <Spinner />
          </Bullseye>
        </CardBody>
      </Card>
    );
  }
  if (hasError) {
    return (
      <Card className="tiles-card">
        <CardTitle>Device summary information</CardTitle>
        <CardBody>{data}</CardBody>
      </Card>
    );
  }
  return (
    <DeviceSummaryTileBase
      orphaned={data['orphaned']}
      active={data['active']}
      noReports={data['noReports']}
      neverReported={data['neverReported']}
    />
  );
}
Example #16
Source File: index.jsx    From NeteaseCloudMusic with MIT License 5 votes vote down vote up
BannerComp = memo((props) => {
  const [currIndex, setCurrIndex] = useState(0);
  const dispatch = useDispatch();
  useEffect(() => {
    dispatch(getBannerListAsyncAction());
  }, [dispatch]);
  const { banners } = useSelector(
    (state) => ({
      banners: state.getIn(["recommendReducer", "bannersList"]),
    }),
    shallowEqual
  );
  const bannerRef = useRef();
  const bannerChange = useCallback((from, to) => {
    // console.log(from);

    setCurrIndex(to);
  }, []);
  const bgImage =
    banners[currIndex] &&
    `${banners[currIndex]["imageUrl"]}?imageView&blur=40x20`;
  return (
    <WrapperContainer bgImage={bgImage}>
      <section className="banner wrap_730_center">
        <LeftContainer>
          <Carousel
            effect="fade"
            beforeChange={bannerChange}
            ref={bannerRef}
            autoplay
          >
            {banners.map((item, index) => {
              return (
                <div className="banner-item" key={item.imageUrl}>
                  <img
                    className="image"
                    src={item.imageUrl}
                    alt={item.typeTitle}
                  />
                </div>
              );
            })}
          </Carousel>
        </LeftContainer>
        <RightContainer></RightContainer>
        <BannerControl>
          <a className="left btn" onClick={(e) => bannerRef.current.prev()}></a>
          <button
            className="right btn"
            onClick={(e) => bannerRef.current.next()}
          ></button>
        </BannerControl>
      </section>
    </WrapperContainer>
  );
})
Example #17
Source File: ImageTable.js    From edge-frontend with Apache License 2.0 5 votes vote down vote up
ImageTable = ({ openCreateWizard, openUpdateWizard }) => {
  const { count, data, isLoading, hasError } = useSelector(
    ({ edgeImagesReducer }) => ({
      count: edgeImagesReducer?.data?.count || 0,
      data: edgeImagesReducer?.data?.data || null,
      isLoading:
        edgeImagesReducer?.isLoading === undefined
          ? true
          : edgeImagesReducer.isLoading,
      hasError: edgeImagesReducer?.hasError,
    }),
    shallowEqual
  );

  const isFinalStatus = (status) => status === 'SUCCESS' || status === 'ERROR';

  const actionResolver = (rowData) => {
    const actionsArray = [];
    if (rowData?.isoURL) {
      actionsArray.push({
        title: (
          <Text
            className="force-text-black remove-underline"
            component="a"
            href={rowData.isoURL}
            rel="noopener noreferrer"
            target="_blank"
          >
            Download
          </Text>
        ),
      });
    }
    if (isFinalStatus(rowData.imageStatus)) {
      actionsArray.push({
        title: 'Update Image',
        onClick: (_event, _rowId, rowData) => {
          openUpdateWizard(rowData.id);
        },
      });
    } else if (rowData?.id) {
      actionsArray.push({
        title: '',
      });
    }

    return actionsArray;
  };

  const areActionsDisabled = (rowData) => !isFinalStatus(rowData.imageStatus);

  return (
    <GeneralTable
      apiFilterSort={true}
      filters={defaultFilters}
      loadTableData={loadEdgeImages}
      tableData={{ count, data, isLoading, hasError }}
      columnNames={columnNames}
      rows={data ? createRows(data) : []}
      emptyStateMessage="No images found"
      emptyStateActionMessage="Create new image"
      emptyStateAction={openCreateWizard}
      actionResolver={actionResolver}
      areActionsDisabled={areActionsDisabled}
      defaultSort={{ index: 4, direction: 'desc' }}
      toolbarButtons={[
        {
          title: 'Create new image',
          click: () => openCreateWizard(),
        },
      ]}
    />
  );
}
Example #18
Source File: my-profile.js    From what-front with MIT License 5 votes vote down vote up
MyProfile = () => {
  const { currentUser } = useSelector(currentUserSelector, shallowEqual);

  const history = useHistory();

  return (
    <div className={styles.wrapper}>
      <div className="container card shadow col-10">
        <div className="container pb-2">
          <h3 className="pt-3">My Profile</h3>
          <hr />
          <div className="row mt-3">
            <div className="col-sm-4 font-weight-bold pb-1">First Name:</div>
            <div className="col-sm-8">{currentUser?.first_name}</div>
          </div>
          <hr />
          <div className="row mt-3">
            <div className="col-sm-4 font-weight-bold pb-1">Last Name:</div>
            <div className="col-sm-8">{currentUser?.last_name}</div>
          </div>
          <hr />
          <div className="row mt-3">
            <div className="col-sm-4 font-weight-bold pb-1">Email address:</div>
            <div className="col-sm-8">{currentUser?.email}</div>
          </div>
          <hr />
          <div className="row mb-2">
            <div className="col-sm-6 offset-sm-6 d-flex justify-content-end">
              <Button
                className={styles.button}
                onClick={() => (history.push(paths.CHANGE_PASSWORD))}
              >
                <span>Change password</span>
              </Button>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}
Example #19
Source File: HouseRulesContainer.js    From airdnd-frontend with MIT License 5 votes vote down vote up
HouseRulesContainer = () => {
  const {
    id,
    address,
    checkinTime,
    checkoutTime,
    rules,
    checkin,
    checkout,
    adult,
    child,
    infant,
  } = useSelector(
    state => ({
      id: state.home.homeState.home.id,
      address: state.home.homeState.home.address,
      checkinTime: state.home.homeState.home.checkin,
      checkoutTime: state.home.homeState.home.checkout,
      rules: state.home.homeState.home.notice.rules,
      checkin: state.reservation.checkin,
      checkout: state.reservation.checkout,
      adult: state.reservation.guests.adult,
      child: state.reservation.guests.child,
      infant: state.reservation.guests.infant,
    }),
    shallowEqual,
  );

  const history = useHistory();
  const [readMore, setReadMore] = useState(false);

  const shortAddress = address.split(',')[0];

  const onReadMore = () => setReadMore(!readMore);
  const onNextPage = () =>
    history.push(
      `/Reservation/GuestInfo/${id}?${checkin && 'checkIn=' + checkin}${
        checkout && '&checkOut=' + checkout
      }${+adult ? '&adult=' + adult : ''}${+child ? '&child=' + child : ''}${
        +infant ? '&infant=' + infant : ''
      }`,
    );

  return (
    <HouseRules
      address={shortAddress}
      checkinTime={checkinTime}
      checkoutTime={checkoutTime}
      rules={rules}
      readMore={readMore}
      onReadMore={onReadMore}
      onNextPage={onNextPage}
      checkin={checkin}
      checkout={checkout}
    />
  );
}
Example #20
Source File: index.jsx    From react-firebase-admin with MIT License 4 votes vote down vote up
UserForm = ({ isEditing, isProfile, user, onSubmitHandler, schema }) => {
  const { loading, success } = useSelector(
    (state) => ({
      loading: state.users.loading,
      success: state.users.success,
    }),
    shallowEqual
  );

  const dispatch = useDispatch();

  const { register, handleSubmit, errors, control, watch, setValue } = useForm({
    defaultValues: { ...user },
    resolver: yupResolver(schema),
  });

  useEffect(() => {
    if (success) {
      setValue('file', null);
    }
    return () => dispatch(usersCleanUp());
  }, [dispatch, success, setValue]);

  const invalidEmailMessage = useFormatMessage('UserForm.invalidEmail');

  const imagePreviewUrl =
    watch('file') && watch('file')[0]
      ? URL.createObjectURL(watch('file')[0])
      : user.logoUrl;

  const goBackMessage = useFormatMessage('UserForm.goBack');

  const pickAnotherFileMessage = useFormatMessage('UserForm.pickAnotherFile');
  const pickFileMessage = useFormatMessage('UserForm.pickFile');

  const emailMessage = useFormatMessage('UserForm.email');

  const adminMessage = useFormatMessage('UserForm.admin');

  return (
    <>
      <div className="tile is-ancestor">
        <div className="tile is-parent">
          <div className="card tile is-child">
            <header className="card-header">
              <p className="card-header-title">
                <span className="icon">
                  <i className="mdi mdi-account-edit default" />
                </span>
                {useFormatMessage('UserForm.userInfo')}
              </p>
            </header>
            <div className="card-content">
              <form onSubmit={handleSubmit(onSubmitHandler)}>
                {isEditing ? (
                  <div className="field is-horizontal">
                    <div className="field-label is-normal">
                      <label className="label">{emailMessage}</label>
                    </div>
                    <div className="field-body">
                      <div className="field">
                        <div className="control">
                          <input
                            type="text"
                            readOnly="readOnly"
                            className="input is-static"
                            name="email"
                            ref={register}
                          />
                        </div>
                      </div>
                    </div>
                  </div>
                ) : (
                  <>
                    <div className="field is-horizontal">
                      <div className="field-label is-normal">
                        <label className="label">{emailMessage}</label>
                      </div>
                      <div className="field-body">
                        <div className="field">
                          <div className="control">
                            <input
                              className={classNames(`input`, {
                                'is-danger': errors.email,
                              })}
                              ref={register}
                              name="email"
                            />
                          </div>
                        </div>
                      </div>
                    </div>
                    {errors.email && (
                      <div className="field is-horizontal">
                        <div className="field-label is-normal" />
                        <div className="field-body">
                          <ErrorMessage text={invalidEmailMessage} />
                        </div>
                      </div>
                    )}
                  </>
                )}

                <div className="field is-horizontal">
                  <div className="field-label is-normal">
                    <label className="label">
                      {useFormatMessage('UserForm.name')}
                    </label>
                  </div>
                  <div className="field-body">
                    <div className="field">
                      <div className="control">
                        <input
                          name="name"
                          id="name"
                          className={classNames('input', {
                            'is-danger': errors.name,
                          })}
                          ref={register}
                          type="text"
                        />
                      </div>
                    </div>
                  </div>
                </div>
                {errors.name && (
                  <div className="field is-horizontal">
                    <div className="field-label is-normal" />
                    <div className="field-body">
                      <ErrorMessage />
                    </div>
                  </div>
                )}
                <div className="field is-horizontal">
                  <div className="field-label is-normal">
                    <label className="label">
                      {useFormatMessage('UserForm.location')}
                    </label>
                  </div>
                  <div className="field-body">
                    <div className="field">
                      <div className="control">
                        <input
                          className="input"
                          type="text"
                          ref={register}
                          name="location"
                        />
                      </div>
                    </div>
                  </div>
                </div>
                {!isProfile && (
                  <div className="field has-check is-horizontal">
                    <div className="field-label">
                      <label className="label">{adminMessage}</label>
                    </div>
                    <div className="field-body">
                      <div className="field">
                        <div className="field">
                          <div className="control">
                            <label className="b-checkbox checkbox">
                              <input
                                type="checkbox"
                                name="isAdmin"
                                ref={register}
                              />
                              <span className="check is-primary" />
                            </label>
                          </div>
                        </div>
                      </div>
                    </div>
                  </div>
                )}

                <div className="field is-horizontal">
                  <div className="field-label is-normal">
                    <label className="label">
                      {useFormatMessage('UserForm.created')}
                    </label>
                  </div>
                  <div className="field-body">
                    <div className="field">
                      <Controller
                        control={control}
                        name="createdAt"
                        render={({ onChange, name, value }) => (
                          <DatePicker
                            name={name}
                            onChange={onChange}
                            date={new Date(value)}
                          />
                        )}
                      />
                    </div>
                  </div>
                </div>

                <hr />

                <div className="field is-horizontal">
                  <div className="field-label is-normal">
                    <label className="label">
                      {useFormatMessage('UserForm.logo')}
                    </label>
                  </div>
                  <div className="field-body">
                    <div className="field">
                      <div className="file has-name">
                        <label className="file-label">
                          <input
                            className="file-input"
                            type="file"
                            name="file"
                            ref={register}
                            accept="image/*"
                          />
                          <span className="file-cta">
                            <span className="file-icon">
                              <i className="mdi mdi-upload" />
                            </span>
                            <span className="file-label">
                              {watch('file') && watch('file').file
                                ? pickAnotherFileMessage
                                : pickFileMessage}
                            </span>
                          </span>
                          <span className="file-name">
                            {watch('file') && watch('file')[0]?.name}
                          </span>
                        </label>
                      </div>
                    </div>
                  </div>
                </div>

                <hr />
                <div className="field is-horizontal">
                  <div className="field-label" />
                  <div className="field-body">
                    <div className="field">
                      <div className="field is-grouped">
                        <div className="control">
                          <button
                            type="submit"
                            className={`button is-primary ${
                              loading && 'is-loading'
                            }`}
                          >
                            <span>{useFormatMessage('UserForm.submit')}</span>
                          </button>
                        </div>
                        {!isProfile && (
                          <Link to={paths.USERS} className="button">
                            {goBackMessage}
                          </Link>
                        )}
                      </div>
                    </div>
                  </div>
                </div>
              </form>
            </div>
          </div>
        </div>
        <div className="tile is-parent preview">
          <div className="card tile is-child">
            <header className="card-header">
              <p className="card-header-title">
                <span className="icon">
                  <i className="mdi mdi-account default" />
                </span>
                {useFormatMessage('UserForm.userPreview')}
              </p>
            </header>
            <div className="card-content">
              {imagePreviewUrl && (
                <>
                  <div className="is-user-avatar image has-max-width is-aligned-center">
                    <img
                      className="user-avatar"
                      src={imagePreviewUrl}
                      alt="User profile logo preview"
                    />
                  </div>
                  <hr />
                </>
              )}

              {!isEditing && (
                <div className="field">
                  <label className="label">{emailMessage}</label>
                  <div className="control is-clearfix">
                    <input
                      data-testid="email"
                      type="text"
                      readOnly="readOnly"
                      className="input is-static"
                      value={watch('email')}
                    />
                  </div>
                </div>
              )}

              <div className="field">
                <label className="label">
                  {useFormatMessage('UserForm.name')}
                </label>
                <div className="control is-clearfix">
                  <input
                    data-testid="name"
                    type="text"
                    readOnly="readOnly"
                    className="input is-static"
                    value={watch('name')}
                  />
                </div>
              </div>

              <div className="field">
                <label className="label">
                  {useFormatMessage('UserForm.location')}
                </label>
                <div className="control is-clearfix">
                  <input
                    data-testid="location"
                    type="text"
                    readOnly="readOnly"
                    className="input is-static"
                    value={watch('location')}
                  />
                </div>
              </div>

              {!isProfile && (
                <div className="field">
                  <label className="label">{adminMessage}</label>
                  <div className="control is-clearfix" data-testid="admin">
                    {watch('isAdmin') ? (
                      <span className="icon">
                        <i className="mdi mdi-check" />
                      </span>
                    ) : (
                      <span className="icon">
                        <i className="mdi mdi-close" />
                      </span>
                    )}
                  </div>
                </div>
              )}

              <div className="field">
                <label className="label">
                  {useFormatMessage('UserForm.created')}
                </label>
                <div className="control is-clearfix" data-testid="date">
                  <p className="date">
                    {useFormatDate(watch('createdAt'), {
                      weekday: 'short',
                      year: 'numeric',
                      month: 'short',
                      day: 'numeric',
                    })}
                  </p>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </>
  );
}
Example #21
Source File: schedule.js    From what-front with MIT License 4 votes vote down vote up
Schedule = () => {
  const [
    dispatchFetchSchedules,
    dispatchFetchGroups,
  ] = useActions([fetchSchedules, globalLoadStudentGroups]);

  const { currentUser } = useSelector(currentUserSelector, shallowEqual);

  const {
    data: schedules,
    isLoading: areSchedulesLoading,
    isLoaded: areSchedulesLoaded,
  } = useSelector(schedulesSelector, shallowEqual);
  const {
    data: groups,
    isLoading: areGroupsLoading,
    isLoaded: areGroupsLoaded,
  } = useSelector(loadStudentGroupsSelector, shallowEqual);

  const [currentWeek, setCurrentWeek] = useState([]);
  const [chosenDate, setChosenDate] = useState(new Date());

  const history = useHistory();

  const DAY_IN_MILLIS = 86400000;
  const WEEK_IN_MILLIS = DAY_IN_MILLIS * 7;

  useEffect(() => {
    dispatchFetchSchedules();
    dispatchFetchGroups();
  }, [dispatchFetchSchedules, dispatchFetchGroups]);

  useEffect(() => {
    const addZero = (num) => (num < 10 ? `0${num}` : num);

    if (areSchedulesLoaded && areGroupsLoaded) {
      const weekDayNames = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
      const firstDayOfWeek = new Date(chosenDate.getTime()
        - (chosenDate.getDay() - 1) * DAY_IN_MILLIS);

      const weekDays = weekDayNames.map((day, index) => {
        const currentDay = new Date(firstDayOfWeek.getTime() + index * DAY_IN_MILLIS);

        const lessons = schedules
          .filter((schedule) => schedule.dayNumber === index || schedule.repeatRate === 1)
          .sort((lesson, nextLesson) => (nextLesson.lessonStart < lesson.lessonStart ? 1 : -1));

        return {
          id: index,
          day,
          lessons,
          date: `${addZero(currentDay.getDate())}.${addZero(currentDay.getMonth() + 1)}`,
          isToday: new Date().toDateString() === currentDay.toDateString(),
          isPast: new Date(new Date().toDateString()).getTime() > currentDay.getTime(),
        };
      });

      setCurrentWeek(weekDays);
    }
  }, [areGroupsLoaded, areSchedulesLoaded, chosenDate, schedules, setCurrentWeek]);

  const handleNextWeek = () => {
    setChosenDate(new Date(chosenDate.getTime() + WEEK_IN_MILLIS));
  };

  const handlePreviousWeek = () => {
    setChosenDate(new Date(chosenDate.getTime() - WEEK_IN_MILLIS));
  };

  const handleInputDate = (event) => {
    setChosenDate(new Date(event.target.value));
  };

  const handleEditSchedule = (id) => {
    history.push(`${paths.SCHEDULE_EDIT}/${id}`);
  };

  const handleAddSchedule = () => {
    history.push(paths.SCHEDULE_ADD);
  };

  return (
    <div className="container">
      <WithLoading isLoading={areGroupsLoading || areSchedulesLoading} className="d-block mx-auto">
        <div className="row mb-4">
          <div className="col-3 d-flex justify-content-start pl-0">
            <input
              type="date"
              min={`${new Date().getFullYear() - 1}-${new Date().getMonth() + 1}-${new Date().getDate()}`}
              max={`${new Date().getFullYear() + 1}-${new Date().getMonth() + 1}-${new Date().getDate()}`}
              onChange={handleInputDate}
              className={styles['date-input']}
            />
            <Button
              className="ml-2"
              variant="warning"
              onClick={() => setChosenDate(new Date())}
            >
              Today
            </Button>
          </div>
          <div className="col-6 d-flex justify-content-center">
            <button
              type="button"
              className={styles['change-week-btn']}
              onClick={handlePreviousWeek}
            >
              <Icon icon="Arrow" className={classNames(styles.arrow, styles['arrow-left'])} />
            </button>
            <h4 className="mb-0">{currentWeek[0]?.date} - {currentWeek[currentWeek.length - 1]?.date}</h4>
            <button
              type="button"
              className={styles['change-week-btn']}
              onClick={handleNextWeek}
            >
              <Icon icon="Arrow" className={styles.arrow} />
            </button>
          </div>
          {[3, 4].includes(currentUser.role) ? (
            <div className="col-3 d-flex justify-content-end pr-0">
              <Button variant="warning" onClick={handleAddSchedule}>
                <Icon icon="Plus" className="icon" />
                Add schedule
              </Button>
            </div>
          ) : null}
        </div>
        <section className={classNames('row', 'justify-content-center')}>
          { currentWeek.map(({ id, isToday, day, date, lessons, isPast }) => (
            <div key={id} className={classNames('col', 'px-0', { [styles['current-day']]: isToday }, styles['day-column'])}>
              <hgroup className={styles['day-column-header']}>
                <h5 className="text-center">{day}</h5>
                <h5 className="text-center">{date}</h5>
              </hgroup>
              <ul className={styles['lessons-list']}>
                { lessons.map(({ id: lessonId, studentGroupId, lessonEnd, lessonStart }) => (
                  <li key={lessonId} className={styles['lessons-list__item']}>
                    <p className={styles['lessons-list__group-name']}>
                      {groups.find((group) => studentGroupId === group.id).name}
                    </p>
                    <div className={styles['lessons-list__details']}>
                      <Badge
                        variant={classNames(
                          { primary: isToday },
                          { secondary: isPast && !isToday },
                          { warning: !isToday && !isPast },
                        )}
                      >
                        {lessonStart.substring(0, 5)} - {lessonEnd.substring(0, 5)}
                      </Badge>
                      {[3, 4].includes(currentUser.role) ? (
                        <button
                          type="button"
                          className={styles['edit-button']}
                          onClick={() => handleEditSchedule(lessonId)}
                        >
                          <Icon icon="Edit" viewBox="0 0 45 45" size={20} />
                        </button>
                      ) : null}
                    </div>
                  </li>
                )) }
              </ul>
            </div>
          )) }
        </section>
      </WithLoading>
    </div>
  );
}
Example #22
Source File: index.jsx    From react-firebase-admin with MIT License 4 votes vote down vote up
Login = () => {
  const { error, isAuth, loading, locale } = useSelector(
    (state) => ({
      error: state.auth.error,
      isAuth: !!state.auth.userData.id,
      loading: state.auth.loading,
      locale: state.preferences.locale,
    }),
    shallowEqual
  );

  const dispatch = useDispatch();

  const { register, handleSubmit, errors, watch } = useForm({
    defaultValues: {},
    resolver: yupResolver(schema),
  });

  useEffect(() => {
    document.documentElement.classList.remove(
      'has-aside-left',
      'has-navbar-fixed-top'
    );
    return () => {
      document.documentElement.classList.add(
        'has-aside-left',
        'has-navbar-fixed-top'
      );
      dispatch(authCleanUp());
    };
  }, [dispatch]);

  const isEmailLink = firebase
    .auth()
    .isSignInWithEmailLink(window.location.href);

  const onSubmitHandler = ({ email, password }) => {
    if (isEmailLink) {
      dispatch(setPassword(email, password, window.location.href));
    } else {
      dispatch(auth(email, password));
    }
  };

  const onSignInSuccessHandler = (authResult) => {
    dispatch(authWithSocialMedia(authResult));
  };

  const onSignInFailHandler = (signInEror) => {
    const signInErrorMessage = firebaseError(signInEror.code, locale);
    toastr.error('', signInErrorMessage);
  };

  const isValidPassword = watch('password') && watch('password').length >= 6;

  const invalidEmailMessage = useFormatMessage('Login.invalidEmail');

  const safePasswordMessage = useFormatMessage('Login.safePassword');

  const unsafePasswordMessage = useFormatMessage('Login.unsafePassword');

  const redirect = isAuth && <Redirect to={paths.ROOT} />;

  const setNewPasswordMessage = useFormatMessage('Login.setNewPassword');

  const loginMessage = useFormatMessage('Login.login');

  const setPasswordMessage = useFormatMessage('Login.setPassword');

  const forgotPasswordMessage = useFormatMessage('Login.forgotPassword');

  const invalidPasswordMessage = useFormatMessage('Login.invalidPassword');

  return (
    <section className="section hero is-fullheight is-error-section">
      {redirect}
      <div className="hero-body">
        <div className="container">
          <div className="columns is-centered">
            <div className="column is-two-fifths">
              <div className="card has-card-header-background">
                <header className="card-header">
                  <p className="card-header-title">
                    <span className="icon">
                      <i className="mdi mdi-lock default" />
                    </span>
                    <span>
                      {isEmailLink ? setNewPasswordMessage : loginMessage}
                    </span>
                  </p>
                </header>
                <div className="card-content">
                  <form onSubmit={handleSubmit(onSubmitHandler)}>
                    <div className="field">
                      <p className="label">{useFormatMessage('Login.email')}</p>
                      <div className="control is-clearfix">
                        <input
                          className={classNames('input', {
                            'is-danger': errors.email,
                          })}
                          name="email"
                          ref={register}
                        />
                      </div>
                      {errors.email && (
                        <ErrorMessage text={invalidEmailMessage} />
                      )}
                    </div>
                    <div className="field">
                      <p className="label">
                        {useFormatMessage('Login.password')}
                      </p>
                      <div className="control is-clearfix">
                        <input
                          className={classNames(
                            'input',
                            {
                              'is-danger': errors.password,
                            },
                            {
                              'is-success': isEmailLink && isValidPassword,
                            }
                          )}
                          type="password"
                          name="password"
                          ref={register}
                        />
                      </div>
                      {errors.password ? (
                        <ErrorMessage
                          text={
                            isEmailLink
                              ? unsafePasswordMessage
                              : invalidPasswordMessage
                          }
                        />
                      ) : (
                        isEmailLink &&
                        isValidPassword && (
                          <p className="is-success">{safePasswordMessage}</p>
                        )
                      )}
                    </div>
                    <br />
                    <div className="field is-grouped">
                      <div className="control">
                        <button
                          type="submit"
                          className={classNames('button', 'is-black', {
                            'is-loading': loading,
                          })}
                        >
                          {isEmailLink ? setPasswordMessage : loginMessage}
                        </button>
                      </div>
                      {!isEmailLink && (
                        <div className="control">
                          <Link
                            to={paths.RESET_PASSWORD}
                            className="button is-outlined"
                          >
                            {forgotPasswordMessage}
                          </Link>
                        </div>
                      )}
                    </div>
                    {error && (
                      <p
                        className={classNames(
                          'has-text-danger',
                          classes.errorMessage
                        )}
                      >
                        {error}
                      </p>
                    )}
                  </form>
                  {!isEmailLink && (
                    <>
                      <hr />
                      <div
                        className={classNames(
                          'field',
                          'is-grouped',
                          classes.socialButtons
                        )}
                      >
                        <StyledFirebaseAuth
                          uiConfig={uiConfig(
                            onSignInSuccessHandler,
                            onSignInFailHandler
                          )}
                          firebaseAuth={firebase.auth()}
                        />
                      </div>
                    </>
                  )}
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}
Example #23
Source File: list-of-students.js    From what-front with MIT License 4 votes vote down vote up
ListOfStudents = () => {
  const {
    data: activeStudents,
    isLoading: areActiveStudentsLoading,
    error: activeStudentsError,
  } = useSelector(activeStudentsSelector, shallowEqual);

  const {
    data: allStudents,
    isLoading: areAllStudentsLoading,
    error: allStudentsError,
  } = useSelector(studentsSelector, shallowEqual);

  const { currentUser } = useSelector(currentUserSelector, shallowEqual);

  const [dispatchLoadStudents, dispatchLoadActiveStudents, dispatchAddAlert] =
    useActions([loadStudents, loadActiveStudents, addAlert]);

  const history = useHistory();

  const [students, setStudents] = useState([]);
  const [visibleStudents, setVisibleStudents] = useState([]);

  const INITIAL_CATEGORIES = [
    { id: 0, name: 'firstName', sortedByAscending: false, tableHead: 'Name' },
    { id: 1, name: 'lastName', sortedByAscending: false, tableHead: 'Surname' },
    { id: 2, name: 'email', sortedByAscending: false, tableHead: 'Email' },
  ];

  const [sortingCategories, setSortingCategories] =
    useState(INITIAL_CATEGORIES);
  const [isShowDisabled, setIsShowDisabled] = useState(false);
  const [searchFieldValue, setSearchFieldValue] = useState('');
  const [showBlocks, setShowBlocks] = useState(false);


  const getDisabledStudents = () => {
    const activeStudentIds = activeStudents.map(({ id }) => id);

    return allStudents.filter(({ id }) => !activeStudentIds.includes(id));
  };

  const searchStudents = (searchedStudents, value) =>
    searchedStudents.filter(({ firstName, lastName }) =>
      `${firstName} ${lastName}`.toLowerCase().includes(value.toLowerCase())
    );

  const changeActiveCategory = (categories, activeCategoryName) =>
    categories.map((category) => {
      if (category.name === activeCategoryName) {
        return { ...category, sortedByAscending: !category.sortedByAscending };
      }
      return { ...category, sortedByAscending: false };
    });

  const downloadStudents = () => {
    history.push(paths.STUDENTS_BY_GROUP_ID);
  };

  useEffect(() => {
    dispatchLoadActiveStudents();
  }, [dispatchLoadActiveStudents]);

  useEffect(() => {
    if (isShowDisabled && allStudents.length && !areAllStudentsLoading) {
      const disabledStudents = getDisabledStudents();

      setStudents(
        disabledStudents.map((student, index) => ({ index, ...student }))
      );
    }
    if (!isShowDisabled && activeStudents.length && !areActiveStudentsLoading) {
      setStudents(
        activeStudents.map((student, index) => ({ index, ...student }))
      );
    }
    setSortingCategories(INITIAL_CATEGORIES);
  }, [
    activeStudents,
    areActiveStudentsLoading,
    allStudents,
    areAllStudentsLoading,
    isShowDisabled,
  ]);

  useEffect(() => {
    if (allStudentsError || activeStudentsError) {
      dispatchAddAlert(allStudentsError || activeStudentsError);
    }
  }, [activeStudentsError, allStudentsError, dispatchAddAlert]);

  useEffect(() => {
    if (isShowDisabled) {
      const disabledStudents = getDisabledStudents();
      const searchedStudents = searchStudents(
        disabledStudents,
        searchFieldValue
      );

      setStudents(
        searchedStudents.map((student, index) => ({ index, ...student }))
      );
    } else {
      const searchedStudents = searchStudents(activeStudents, searchFieldValue);

      setStudents(
        searchedStudents.map((student, index) => ({ index, ...student }))
      );
    }
  }, [searchFieldValue, isShowDisabled]);

  const handleSortByParam = (data, categoryParams) => {
    const sortedStudents = data;
    setSortingCategories(
      changeActiveCategory(sortingCategories, categoryParams.sortingParam)
    );
    setStudents(sortedStudents);
  };

  const handleShowDisabled = (event) => {
    setIsShowDisabled(!isShowDisabled);

    if (event.target.checked) {
      dispatchLoadStudents();
    } else {
      dispatchLoadActiveStudents();
    }
  };

  const handleEdit = (event, id) => {
    event.stopPropagation();
    history.push(`${paths.STUDENT_EDIT}/${id}`);
  };

  const handleDetails = (id) => {
    history.push(`${paths.STUDENTS_DETAILS}/${id}`);
  };

  const handleSearch = (value) => {
    setSearchFieldValue(value);
  };

  const handleAddStudent = () => {
    history.push(paths.UNASSIGNED_USERS);
  };

  const listProps = {
    data: visibleStudents,
    handleDetails,
    handleEdit,
    errors: [
      {
        message: 'Loading has been failed',
        check: [!!allStudentsError, !!activeStudentsError],
      },
      {
        message: 'Student is not found',
        check: [!visibleStudents.length && !!searchFieldValue],
      },
    ],
    access: true,
    fieldsToShow: ['firstName', 'lastName', 'email', 'edit'],
  };

 

  return (
    <div className="container pt-5">
      <div className="row justify-content-between align-items-center mb-3">
        <h2 className="col-6">Students</h2>
        <div className="col-2 text-right">
          {!areActiveStudentsLoading &&
            !areAllStudentsLoading &&
            `${visibleStudents.length} of ${students.length} students`}
        </div>
        
      </div>
      <div className="row mr-0">
        <div className="col-12 card shadow p-3 mb-5 bg-white ml-2 mr-2">
          <div className="row align-items-center d-flex justify-content-between mt-2 mb-3">
            <div className="col-2">
              <div className="btn-group">
                <button
                  type="button"
                  className="btn btn-secondary"
                  disabled={!showBlocks}
                  onClick={() => setShowBlocks(false)}
                >
                  <Icon icon="List" color="#2E3440" size={25} />
                </button>
                <button
                  type="button"
                  className="btn btn-secondary"
                  disabled={showBlocks}
                  onClick={() => setShowBlocks(true)}
                >
                  <Icon icon="Card" color="#2E3440" size={25} />
                </button>
              </div>
            </div>
            <div className="col-3">
              <Search
                value={searchFieldValue}
                onSearch={handleSearch}
                placeholder="student's name"
              />
            </div>
            <div className="col-2 custom-control custom-switch text-right">
              <input
                value={isShowDisabled}
                type="checkbox"
                className={classNames(
                  'custom-control-input',
                  styles['custom-control-input']
                )}
                id="show-disabled-check"
                onChange={handleShowDisabled}
              />
              <label
                className={classNames(
                  'custom-control-label',
                  styles['custom-control-label']
                )}
                htmlFor="show-disabled-check"
              >
                Disabled students
              </label>
            </div>
            
            {[8, 4].includes(currentUser.role) && (
              <div className={classNames('col-4 text-right', styles['buttons-block'])}>
                <Button
                  onClick={downloadStudents}
                  type="button"
                  className={classNames(
                    'btn btn-warning ',
                    styles['left-add-btn']
                  )}
                >
                  Upload student('s)
                </Button>
                <Button onClick={handleAddStudent}>
                  <span>Add a student</span>
                </Button>
              </div>
            )}
          </div>
          <WithLoading
            isLoading={areActiveStudentsLoading || areAllStudentsLoading}
            className="d-block mx-auto my-2"
          >
            {showBlocks ? (
              <div className="container d-flex flex-wrap">
                <List listType={'block'} props={listProps} />
              </div>
            ) : (
              <Table
                sortingCategories={sortingCategories}
                currentUser={currentUser}
                onClick={handleSortByParam}
                data={students}
                access={{ unruledUser: [2], unassigned: '' }}
              >
                <List listType="list" props={listProps} />
              </Table>
            )}
          </WithLoading>
        </div>
        <div
          className={classNames(
            'row justify-content-between align-items-center mb-3',
            styles.paginate
          )}
        >
          <Pagination
            items={students}
            setVisibleItems={setVisibleStudents}
          />
        </div>
      </div>
    </div>
  );
}
Example #24
Source File: index.jsx    From react-firebase-admin with MIT License 4 votes vote down vote up
ChangePasswordCard = () => {
  const { loading, changedPassword } = useSelector(
    (state) => ({
      loading: state.auth.loading,
      changedPassword: state.auth.changedPassword,
    }),
    shallowEqual
  );

  const dispatch = useDispatch();

  const { register, handleSubmit, watch, setValue, errors } = useForm({
    mode: 'onChange',
    defaultValues: {
      current: '',
      new: '',
      confirmation: '',
    },
    resolver: yupResolver(schema),
  });

  useEffect(() => {
    if (changedPassword) {
      setValue('current', '');
      setValue('new', '');
      setValue('confirmation', '');
    }
    return () => dispatch(authCleanUp());
  }, [dispatch, changedPassword, setValue]);

  const newPassword = watch('new');
  const confirmationPassword = watch('confirmation');

  const invalidPasswordMessage = useFormatMessage(
    `ChangePassword.invalidPassword`
  );

  const safePasswordMessage = useFormatMessage(`ChangePassword.safePassword`);

  const insecurePasswordMessage = useFormatMessage(
    `ChangePassword.insecurePassword`
  );

  const passwordsMatchMessagge = useFormatMessage(
    `ChangePassword.matchPassword`
  );

  const notMatchPasswordMessage = useFormatMessage(
    `ChangePassword.notMatchPassword`
  );

  const samePasswordMessage = useFormatMessage(`ChangePassword.samePassword`);

  const onSubmitHandler = ({ current, confirmation }) => {
    dispatch(changeUserPassword(current, confirmation));
  };

  return (
    <div className="card">
      <header className="card-header">
        <p className="card-header-title">
          <span className="icon">
            <i className="fa fa-lock" />
          </span>
          {useFormatMessage(`ChangePassword.changePassword`)}
        </p>
      </header>
      <div className="card-content">
        <form onSubmit={handleSubmit(onSubmitHandler)}>
          <div className="field is-horizontal">
            <div className="field-label is-normal">
              <label className="label">
                {useFormatMessage(`ChangePassword.currentPassword`)}
              </label>
            </div>
            <div className="field-body">
              <div className="field">
                <div className="control">
                  <input
                    data-testid="current"
                    className={classNames('input', {
                      'is-danger': errors.current,
                    })}
                    type="password"
                    name="current"
                    ref={register}
                  />
                </div>
                {errors.current && (
                  <ErrorMessage text={invalidPasswordMessage} />
                )}
              </div>
            </div>
          </div>
          <hr />
          <div className="field is-horizontal">
            <div className="field-label is-normal">
              <label className="label">
                {useFormatMessage(`ChangePassword.newPassword`)}
              </label>
            </div>
            <div className="field-body">
              <div className="field">
                <div className="control">
                  <input
                    data-testid="new"
                    className={classNames(
                      `input`,
                      { 'is-success': newPassword && !errors.new },
                      { 'is-danger': errors.new }
                    )}
                    type="password"
                    name="new"
                    ref={register}
                  />
                </div>
                {errors.new ? (
                  <ErrorMessage
                    text={
                      newPassword.length < 6
                        ? insecurePasswordMessage
                        : samePasswordMessage
                    }
                  />
                ) : (
                  newPassword && (
                    <p className="is-success">{safePasswordMessage}</p>
                  )
                )}
              </div>
            </div>
          </div>

          <div className="field is-horizontal">
            <div className="field-label is-normal">
              <label className="label">
                {useFormatMessage(`ChangePassword.confirmPassword`)}
              </label>
            </div>
            <div className="field-body">
              <div className="field">
                <div className="control">
                  <input
                    data-testid="confirmation"
                    className={classNames(
                      `input`,
                      {
                        'is-success':
                          confirmationPassword && !errors.confirmation,
                      },
                      {
                        'is-danger': errors.confirmation,
                      }
                    )}
                    type="password"
                    name="confirmation"
                    ref={register}
                  />
                </div>
                {errors.confirmation ? (
                  <ErrorMessage text={notMatchPasswordMessage} />
                ) : (
                  confirmationPassword && (
                    <p className="is-success">{passwordsMatchMessagge}</p>
                  )
                )}
              </div>
            </div>
          </div>
          <div className="field is-horizontal">
            <div className="field-label is-normal" />
            <div className="field-body">
              <div className="field">
                <div className="control">
                  <button
                    type="submit"
                    className={`button is-primary ${loading && 'is-loading'}`}
                  >
                    {useFormatMessage(`ChangePassword.submits`)}
                  </button>
                </div>
              </div>
            </div>
          </div>
        </form>
      </div>
    </div>
  );
}
Example #25
Source File: mentor-details.js    From what-front with MIT License 4 votes vote down vote up
MentorDetails = ({ id }) => {
  const history = useHistory();
  const {
    data: mentor,
    isLoading: mentorIsLoading,
    isLoaded: mentorIsLoaded,
    error: mentorError
  } = useSelector(mentorIdSelector, shallowEqual);

  const {
    data: mentorGroups,
    isLoading: mentorGroupsAreLoading,
    isLoaded: mentorGroupsAreLoaded,
    error: mentorGroupsError
  } = useSelector(mentorGroupsSelector, shallowEqual);

  const {
    data: mentorCourses,
    isLoading:mentorCoursesAreLoading ,
    isLoaded: mentorCoursesAreLoaded,
    error: mentorCoursesError
  } = useSelector(mentorCoursesSelector, shallowEqual);

  const [
    dispatchLoadMentors,
  ] = useActions([fetchMentorById, fetchActiveMentors]); 

  useEffect(() => {
    if (mentorError && mentorCoursesError && mentorGroupsError) {
      history.push(paths.NOT_FOUND);
    }
  }, [mentorError, mentorCoursesError, mentorGroupsError, history]);

  useEffect(() => {
    dispatchLoadMentors(id);
  }, [dispatchLoadMentors, id]);

  return (
    <div className="container" data-testid='mentorDetails'>
      <div className="row justify-content-center">
        <div className="col-sm-12 card shadow">
          <div className="px-2 py-4">
            <h3>Mentor Details</h3>
            <hr />
            {/* test */}
            <WithLoading isLoading={mentorIsLoading || !mentorIsLoaded}
              className="d-block mx-auto m-0"
            >
              <div className="row">
                <span className="col-12 col-md-6 font-weight-bolder">First Name:</span>
                <span className="col-12 col-md-6" data-testid='firstName'>{mentor?.firstName}</span>
              </div>
              <hr />
              <div className="row">
                <span className="col-12 col-md-6 font-weight-bolder">Last Name:</span>
                <span className="col-12 col-md-6" data-testid='lastName'>{mentor?.lastName}</span>
              </div>
              <hr />
              <div className="row">
                <span className="col-12 col-md-6 font-weight-bolder">Email:</span>
                <span className="col-12 col-md-6" data-testid='email'>{mentor?.email}</span>
              </div>
              <hr />
            <div className="row">
              <div className="col-12 col-md-6 font-weight-bolder"><span>Group('s): </span></div>
              {/* test */}
              <WithLoading
                isLoading={mentorGroupsAreLoading || !mentorGroupsAreLoaded}
                className="d-block mx-auto m-0"
              >
                <div className="col-12 col-md-6 d-flex flex-wrap lead">
                  {mentorGroups
                    .map(({ id, name }) => (
                      <div className="pr-2" key={id}>
                        <Badge pill variant="info">
                          <Link
                            to={`${paths.GROUPS_DETAILS}/${id}`}
                            className="text-decoration-none text-light group-link"
                            data-testid='groupLink'
                            data-testgroupidparam={id}
                          >{name}
                          </Link>
                        </Badge>
                      </div>
                    ))}
                </div>
              </WithLoading>
            </div>
            <hr/>
            <div className="row">
              <div className="col-12 col-md-6 font-weight-bolder"><span>Course('s): </span></div>
              {/* test */}
              <WithLoading
                isLoading={mentorCoursesAreLoading || !mentorCoursesAreLoaded}
                className="d-block mx-auto m-0"
              >
                <div className="col-12 col-md-6 d-flex flex-wrap lead">
                  {mentorCourses
                    .map(({ id, name }) => (
                      <div className="pr-2" key={id}>
                        <Badge pill variant="info">
                          <Link
                            to={`${paths.COURSE_DETAILS}/${id}`}
                            className="text-decoration-none text-light course-link"
                            data-testid='courseLink'
                            data-testcourseidparam={id}
                          >{name}</Link>
                        </Badge>
                      </div>
                    ))}
                </div>
                </WithLoading>
              </div>
            </WithLoading>
          </div>
        </div>
      </div>
    </div>
  );
}
Example #26
Source File: index.jsx    From react-firebase-admin with MIT License 4 votes vote down vote up
NavBar = ({ handleMobileToggle, asideMobileActive }) => {
  const [navMobileActive, setNavMobileActive] = useState(false);

  const { userName, logoUrl, locale } = useSelector(
    (state) => ({
      userName: state.auth.userData.name,
      logoUrl: state.auth.userData.logoUrl,
      locale: state.preferences.locale,
    }),
    shallowEqual
  );

  const dispatch = useDispatch();

  const onClickLogoutHandler = () => {
    dispatch(logout());
  };

  const onMobileToggleHandler = useCallback(() => {
    setNavMobileActive(!navMobileActive);
  }, [setNavMobileActive, navMobileActive]);

  const changeLocaleHandler = (local) => {
    dispatch(setUserLocale(local));
  };

  const locales = availableLocales.filter((local) => local !== locale);

  return (
    <nav id="navbar-main" className="navbar is-fixed-top">
      <div className="navbar-brand">
        <a
          className="navbar-item is-hidden-desktop jb-aside-mobile-toggle"
          onClick={handleMobileToggle}
        >
          <span className="icon">
            <i
              className={classNames(
                'mdi',
                'mdi-24px',
                { 'mdi-forwardburger': !asideMobileActive },
                { 'mdi-backburger': asideMobileActive }
              )}
            />
          </span>
        </a>
      </div>
      <div className="navbar-brand is-right">
        <a
          className="navbar-item is-hidden-desktop jb-navbar-menu-toggle"
          data-target="navbar-menu"
          onClick={onMobileToggleHandler}
        >
          <span className="icon">
            <i
              className={classNames(
                'mdi',
                { 'mdi-dots-vertical': !navMobileActive },
                { 'mdi-close': navMobileActive }
              )}
            />
          </span>
        </a>
      </div>
      <div
        className={classNames('navbar-menu', 'fadeIn', 'animated', 'faster', {
          'is-active': navMobileActive,
        })}
        id="navbar-menu"
      >
        <div className="navbar-end">
          <div className="navbar-item has-dropdown has-dropdown-with-icons has-divider has-user-avatar is-hoverable">
            <a className="navbar-link is-arrowless">
              <div className="is-user-avatar">
                <span>
                  <img id={locale} src={flags[locale]} alt={`${locale} flag`} />
                </span>
              </div>
              <span className="icon">
                <i className="mdi mdi-chevron-down" />
              </span>
            </a>
            <div className="navbar-dropdown">
              {locales.map((local) => (
                <a
                  onClick={() => changeLocaleHandler(local)}
                  className="navbar-item"
                  id={local}
                  key={local}
                >
                  <div className="is-user-avatar">
                    <span>
                      <img src={flags[local]} alt={`${local} flag`} />
                    </span>
                  </div>
                </a>
              ))}
            </div>
          </div>
          <div className="navbar-item has-dropdown has-dropdown-with-icons has-divider has-user-avatar is-hoverable">
            <a className="navbar-link is-arrowless">
              <div className="is-user-avatar">
                <img src={logoUrl || defaultLogo} alt="User profile" />
              </div>
              <div className="is-user-name">
                <span>{userName}</span>
              </div>
              <span className="icon">
                <i className="mdi mdi-chevron-down" />
              </span>
            </a>
            <div className="navbar-dropdown">
              <Link to={paths.PROFILE} onClick={onMobileToggleHandler}>
                <span className="icon">
                  <i className="mdi mdi-account" />
                </span>
                <span>{useFormatMessage('NavBar.profile')}</span>
              </Link>
              <hr className="navbar-divider" />
              <a
                className="navbar-item"
                id="logout"
                onClick={onClickLogoutHandler}
              >
                <span className="icon">
                  <i className="mdi mdi-logout" />
                </span>
                <span>{useFormatMessage('NavBar.logOut')}</span>
              </a>
            </div>
          </div>
        </div>
      </div>
    </nav>
  );
}
Example #27
Source File: index.jsx    From NeteaseCloudMusic with MIT License 4 votes vote down vote up
Recommend = memo(() => {
  const [tabsState, setTabsState] = useState([
    {
      name: "华语",
    },
    {
      name: "流行",
    },
    {
      name: "摇滚",
    },
    {
      name: "民谣",
    },
    {
      name: "电子",
    },
  ]);
  const dispatch = useDispatch();
  const { listData } = useSelector(
    (state) => ({
      listData: state.getIn(["recommendReducer", "personalizedList"]),
    }),
    shallowEqual
  );
  useEffect(() => {
    dispatch(fetchPersonalizedAsyncAction());
  }, [dispatch]);
  return (
    <>
      <BannerComp />
      <section className="recommend_container wrap_980_center">
        <section className="container_left">
          <NRcmdTitleComp title="热门推荐" tabs={tabsState} />
          <section className="lists">
            {listData.map((item) => {
              return <CommonItem key={item.id} itemData={item} />;
            })}
          </section>
          <NRcmdTitleComp title="新碟上架" tag="new" />
          <NewDis />
          <NRcmdTitleComp title="榜单" tag="new" />
          {/* 榜单的主要内容 */}
          <section className="n-bilst">
            <dl className="blk">
              <dt className="top">
                <div className="cover">
                  <img
                    src="http://p3.music.126.net/DrRIg6CrgDfVLEph9SNh7w==/18696095720518497.jpg?param=100y100"
                    data-src="http://p3.music.126.net/DrRIg6CrgDfVLEph9SNh7w==/18696095720518497.jpg?param=100y100"
                    alt=""
                    className="j-img"
                  />
                  <a
                    href="/discover/toplist?id=19723756"
                    className="msk"
                    title="云音乐飙升榜"
                  ></a>
                </div>
                <div className="tit">
                  <a href="/discover/toplist?id=19723756" title="云音乐飙升榜">
                    <h3 className="fs1">云音乐飙升榜</h3>
                  </a>
                  <div className="btn">
                    <a href="" className="play ">
                      播放
                    </a>
                    <a href="" className="favorites">
                      收藏
                    </a>
                  </div>
                </div>
              </dt>
              <dd className="list-container">
                <ol className="">
                  <li>
                    <span className="no no-top">1</span>
                    <a href="" className="nm">
                      是想你的声音啊
                    </a>
                    <div className="oper">
                      <a href="" className="btn play"></a>
                      <a href="" className="btn add"></a>
                      <a href="" className="btn shou"></a>
                    </div>
                  </li>
                </ol>
              </dd>
            </dl>
            <dl className="blk">
              <dt className="top">
                <div className="cover">
                  <img
                    src="http://p3.music.126.net/N2HO5xfYEqyQ8q6oxCw8IQ==/18713687906568048.jpg?param=100y100"
                    data-src="http://p3.music.126.net/DrRIg6CrgDfVLEph9SNh7w==/18696095720518497.jpg?param=100y100"
                    alt=""
                    className="j-img"
                  />
                  <a
                    href="/discover/toplist?id=19723756"
                    className="msk"
                    title="云音乐飙升榜"
                  ></a>
                </div>
                <div className="tit">
                  <a href="/discover/toplist?id=19723756" title="云音乐飙升榜">
                    <h3 className="fs1">云音乐新歌榜</h3>
                  </a>
                  <div className="btn">
                    <a href="" className="play ">
                      播放
                    </a>
                    <a href="" className="favorites">
                      收藏
                    </a>
                  </div>
                </div>
              </dt>
              <dd className="list-container">
                <ol className="">
                  <li>
                    <span className="no no-top">1</span>
                    <a href="" className="nm">
                      是想你的声音啊
                    </a>
                    <div className="oper">
                      <a href="" className="btn play"></a>
                      <a href="" className="btn add"></a>
                      <a href="" className="btn shou"></a>
                    </div>
                  </li>
                </ol>
              </dd>
            </dl>
            <dl className="blk blk1">
              <dt className="top">
                <div className="cover">
                  <img
                    src="http://p3.music.126.net/sBzD11nforcuh1jdLSgX7g==/18740076185638788.jpg?param=100y100"
                    data-src="http://p3.music.126.net/DrRIg6CrgDfVLEph9SNh7w==/18696095720518497.jpg?param=100y100"
                    alt=""
                    className="j-img"
                  />
                  <a
                    href="/discover/toplist?id=19723756"
                    className="msk"
                    title="云音乐飙升榜"
                  ></a>
                </div>
                <div className="tit">
                  <a href="/discover/toplist?id=19723756" title="云音乐飙升榜">
                    <h3 className="fs1">网易原创歌曲榜</h3>
                  </a>
                  <div className="btn">
                    <a href="" className="play ">
                      播放
                    </a>
                    <a href="" className="favorites">
                      收藏
                    </a>
                  </div>
                </div>
              </dt>
              <dd className="list-container">
                <ol className="">
                  <li>
                    <span className="no no-top">1</span>
                    <a href="" className="nm">
                      是想你的声音啊
                    </a>
                    <div className="oper">
                      <a href="" className="btn play"></a>
                      <a href="" className="btn add"></a>
                      <a href="" className="btn shou"></a>
                    </div>
                  </li>
                </ol>
              </dd>
            </dl>
          </section>
        </section>
        <section className="container_right">
          <UserLoginComp />
        </section>
      </section>
    </>
  );
})
Example #28
Source File: TreeMenu.js    From ra-treemenu with MIT License 4 votes vote down vote up
Menu = (props) => {
    const {
        className,
        dense,
        hasDashboard,
        onMenuClick,
        logout,
        dashboardlabel,
        resources,
        ...rest
    } = props;

    const classes = useStyles(props);
    const translate = useTranslate();
    const open = useSelector((state) => state.admin.ui.sidebarOpen);
    const pathname = useSelector((state) => state.router.location.pathname);
    const resources = resources || useSelector(getResources, shallowEqual);
    const hasList = (resource) => (resource.hasList);

    const handleToggle = (parent) => {
        /**
         * Handles toggling of parents dropdowns
         * for resource visibility
         */
        setState(state => ({ [parent]: !state[parent] }));
    };

    const isXSmall = useMediaQuery((theme) =>
        /**
         * This function is not directly used anywhere
         * but is required to fix the following error:
         * 
         * Error: Rendered fewer hooks than expected.
         * This may be caused by an accidental early
         * return statement.
         * 
         * thrown by RA at the time of rendering.
         */
        theme.breakpoints.down('xs')
    );

    const isParent = (resource) => (
        /**
         * Check if the given resource is a parent
         * i.e. dummy resource for menu parenting
         */
        resource.options &&
        resource.options.hasOwnProperty('isMenuParent') &&
        resource.options.isMenuParent
    );

    const isOrphan = (resource) => (
        /**
         * Check if the given resource is an orphan
         * i.e. has no parents defined. Needed as
         * these resources are supposed to be rendered
         * as is
         *  
         */
        resource.options &&
        !resource.options.hasOwnProperty('menuParent') &&
        !resource.options.hasOwnProperty('isMenuParent')
    );

    const isChildOfParent = (resource, parentResource) => (
        /**
         * Returns true if the given resource is the
         * mapped child of the parentResource
         */
        resource.options &&
        resource.options.hasOwnProperty('menuParent') &&
        resource.options.menuParent == parentResource.name
    );
    const geResourceName = (slug) => {
        if (!slug) return;
        var words = slug.toString().split('_');
        for (var i = 0; i < words.length; i++) {
            var word = words[i];
            words[i] = word.charAt(0).toUpperCase() + word.slice(1);
        }
        return words.join(' ');
    }

    const getPrimaryTextForResource = (resource) => {
        let resourcename = '';
        if (resource.options && resource.options.label)
            resourcename = resource.options.label;
        else if (resource.name) {
            resourcename = translate(`resources.${resource.name}.name`);
            if (resourcename.startsWith('resources.'))
                resourcename = geResourceName(resource.name);
        }
        return resourcename;
    }

    const MenuItem = (resource) => (
        /**
         * Created and returns the MenuItemLink object component
         * for a given resource.
         */
        <MenuItemLink
            key={resource.name}
            to={`/${resource.name}`}
            primaryText={getPrimaryTextForResource(resource)}
            leftIcon={
                resource.icon
                    ? <resource.icon />
                    : <DefaultIcon />
            }
            onClick={onMenuClick}
            dense={dense}
            sidebarIsOpen={open}
        />
    );

    /**
     * Mapping a "parent" entry and then all its children to the "tree" layout
     */
    const mapParentStack = (parentResource) => (
        <CustomMenuItem
            key={parentResource.name}
            handleToggle={() => handleToggle(parentResource.name)}
            isOpen={state[parentResource.name] || parentActiveResName === parentResource.name}
            sidebarIsOpen={open}
            name={getPrimaryTextForResource(parentResource)}
            icon={parentResource.icon ? <parentResource.icon /> : <LabelIcon />}
            dense={dense}
        >
            {
                // eslint-disable-next-line
                resources
                    .filter((resource) => isChildOfParent(resource, parentResource) && hasList(resource))
                    .map((childResource) => { return MenuItem(childResource); })
            }
        </CustomMenuItem>
    );

    /**
     * Mapping independent (without a parent) entries
     */
    const mapIndependent = (independentResource) => hasList(independentResource) && MenuItem(independentResource);


    /**
     * Initialising the initialExpansionState and
     * active parent resource name at the time of
     * initial menu rendering.
     */
    const initialExpansionState = {};
    let parentActiveResName = null;

    /**
     * Initialise all parents to inactive first.
     * Also find the active resource name.
     */
    resources.forEach(resource => {
        if (isParent(resource)) {
            initialExpansionState[resource.name] = false;
        } else if (pathname.startsWith(`/${resource.name}`) && resource.options.hasOwnProperty('menuParent')) {
            parentActiveResName = resource.options.menuParent;
        }
    });

    const [state, setState] = useState(initialExpansionState);

    /**
     * The final array which will hold the array
     * of resources to be rendered
     */
    const resRenderGroup = [];

    /**
     * Looping over all resources and pushing the menu tree
     * for rendering in the order we find them declared in
     */
    resources.forEach(r => {
        if (isParent(r)) resRenderGroup.push(mapParentStack(r));
        if (isOrphan(r)) resRenderGroup.push(mapIndependent(r));
    });

    return (
        <div>
            <div
                className={classnames(classes.main, className, {
                    [classes.open]: open,
                    [classes.closed]: !open,
                })}
                {...rest}
            >
                {hasDashboard && (
                    <DashboardMenuItem
                        onClick={onMenuClick}
                        dense={dense}
                        sidebarIsOpen={open}
                        primaryText={dashboardlabel}
                    />
                )}
                {resRenderGroup}
            </div>
        </div>
    );
}
Example #29
Source File: edit-group.js    From what-front with MIT License 4 votes vote down vote up
EditGroup = ({
  id: groupId, studentGroupData, studentsData, mentorsData, coursesData,
}) => {
  const {
    isLoading: isEditing,
    isLoaded: isEdited,
    error: editingError,
  } = useSelector(editStudentGroupSelector, shallowEqual);

  const [dispatchEditGroup, dispatchAddAlert] = useActions([editStudentGroup, addAlert]);

  const history = useHistory();

  const {
    data: group,
    isLoading: isGroupLoading,
    isLoaded: isGroupLoaded,
  } = studentGroupData;
  const {
    data: students,
    isLoading: areStudentsLoading,
    isLoaded: areStudentsLoaded,
  } = studentsData;
  const {
    data: mentors,
    isLoading: areMentorsLoading,
    isLoaded: areMentorsLoaded,
  } = mentorsData;
  const {
    data: courses,
    isLoading: areCoursesLoading,
    loaded: areCoursesLoaded,
  } = coursesData;

  const [groupMentors, setGroupMentors] = useState([]);
  const [mentorInputError, setMentorInputError] = useState('');
  const [groupStudents, setGroupStudents] = useState([]);
  const [studentInputError, setStudentInputError] = useState('');

  const prevGroupMentors = usePrevious(groupMentors);
  const prevGroupStudents = usePrevious(groupStudents);

  function usePrevious(value) {
    const ref = useRef();
    useEffect(() => {
      ref.current = value;
    });
    return ref.current;
  }

  useEffect(() => {
    if (!isEditing && isEdited && !editingError) {
      history.push(paths.GROUPS);
      dispatchAddAlert('The group has been successfully edited!', 'success');
    }
    if (!isEditing && !isEdited && editingError) {
      dispatchAddAlert(editingError);
    }
  }, [isEdited, editingError, isEditing, history, dispatchAddAlert]);

  useEffect(() => {
    if (mentors.length) {
      setGroupMentors(mentors.filter(({ id }) => group.mentorIds?.includes(id)));
    }
  }, [group.mentorIds, mentors]);

  useEffect(() => {
    if (students.length) {
      setGroupStudents(students.filter(({ id }) => group.studentIds?.includes(id)));
    }
  }, [group.studentIds, students]);

  const addMentor = (mentorFullInfo, clearField) => {
    const [selectedMentorEmail] = mentorFullInfo.split(' ').reverse();
    const mentor = mentors.find(({ email }) => email === selectedMentorEmail);

    if (mentor) {
      clearField();
      setMentorInputError('');
      setGroupMentors((prevState) => [...prevState, mentor]);
    } else {
      setMentorInputError('Mentor not found');
    }
  };

  const addStudent = (studentFullInfo, clearField) => {
    const [selectedStudentEmail] = studentFullInfo.split(' ').reverse();
    const student = students.find(({ email }) => email === selectedStudentEmail);

    if (student) {
      clearField();
      setStudentInputError('');
      setGroupStudents((prevState) => [...prevState, student]);
    } else {
      setStudentInputError('Student not found');
    }
  };

  const removeMentor = useCallback((mentorId) => {
    setGroupMentors(groupMentors.filter(({ id }) => id !== mentorId));
  }, [groupMentors]);

  const removeStudent = useCallback((studentId) => {
    setGroupStudents(groupStudents.filter(({ id }) => id !== studentId));
  }, [groupStudents]);

  const handleSubmit = ({
    name, startDate, finishDate, courseId,
  }) => {
    const newGroupData = {
      id: groupId,
      name,
      courseId,
      startDate: commonHelpers.transformDateTime({ isDayTime: false, dateTime: startDate }).formInitialValue,
      finishDate: commonHelpers.transformDateTime({ isDayTime: false, dateTime: finishDate }).formInitialValue,
      studentIds: [...new Set(groupStudents.map(({ id }) => id))],
      mentorIds: [...new Set(groupMentors.map(({ id }) => id))],
    };
    dispatchEditGroup(newGroupData);
  };

  const handleReset = () => {
    setGroupStudents(students.filter(({ id }) => group.studentIds.includes(id)));
    setGroupMentors(mentors.filter(({ id }) => group.mentorIds.includes(id)));
    setStudentInputError('');
    setMentorInputError('');
  };

  return (
    <div className="w-100">
      <div className="justify-content-center">
        <div className="w-100 card shadow p-4">
          <WithLoading
            isLoading={isGroupLoading ||
              areMentorsLoading ||
              areCoursesLoading ||
              areStudentsLoading}
            className={styles['loader-centered']}
          >
            <Formik
              initialValues={{
                name: group.name,
                startDate: commonHelpers.transformDateTime({ isDayTime: false, dateTime: group.startDate }).formInitialValue,
                finishDate: commonHelpers.transformDateTime({ isDayTime: false, dateTime: group.finishDate }).formInitialValue,
                courseId: group.courseId,
                mentor: '',
                student: '',
              }}
              onSubmit={handleSubmit}
              validationSchema={editGroupValidation}
              validateOnMount={false}
            >
              {({ values, errors, setFieldValue, isValid, dirty }) => (
                <Form className="px-2 py-4" name="start-group" data-testid="editGroup">
                  <h3>Group Editing</h3>
                  <hr />
                  <div className="row mb-3 align-items-start">
                    <div className="col d-flex align-items-center">
                      <label className="mt-2" htmlFor="name">Group name:
                      </label>
                    </div>
                    <div className="col-md-8">
                      <Field
                        className={classNames('form-control', { 'border-danger': errors.name })}
                        type="text"
                        name="name"
                        id="group-name"
                        placeholder="group name"
                      />
                      {errors.name && <p className="w-100 text-danger mb-0">{errors.name}</p>}
                    </div>
                  </div>
                  <div className="row mb-3">
                    <div className="col d-flex align-items-center">
                      <label className="mb-0" htmlFor="course">Course:</label>
                    </div>
                    <div className="col-md-8">
                      <Field
                        id="course-name"
                        as="select"
                        className={classNames('custom-select')}
                        name="courseId"
                      >
                        <option value={group.courseId} key={group.courseId}>
                          { courses.find((course) => course.id === group.courseId)?.name }
                        </option>
                        {
                          courses
                            .filter((course) => course.id !== group.courseId)
                            .map((course) => (
                              <option value={course.id} key={course.id}>{course.name}</option>
                            ))
                        }
                      </Field>
                    </div>
                  </div>
                  <div className="row mb-3">
                    <div className="col d-flex align-items-center">
                      <label className="mb-0" htmlFor="start-date">Start date:</label>
                    </div>
                    <div className="col-md-8">
                      <Field
                        className={classNames('form-control', { 'border-danger': errors.startDate })}
                        type="date"
                        name="startDate"
                        id="start-date"
                      />
                      {errors.startDate && <p className="text-danger mb-0">{errors.startDate}</p>}
                    </div>
                  </div>
                  <div className="row mb-3">
                    <div className="col d-flex align-items-start">
                      <label className="mt-2" htmlFor="finish-date">Finish date:</label>
                    </div>
                    <div className="col-md-8">
                      <Field
                        className={classNames('form-control', { 'border-danger': errors.finishDate })}
                        type="date"
                        name="finishDate"
                        id="finish-date"
                      />
                      {errors.finishDate && <p className="text-danger mb-0">{errors.finishDate}</p>}
                    </div>
                  </div>
                  <div className="row mb-3">
                    <div className="col d-flex align-items-start">
                      <label className="mt-2" htmlFor="mentor">Mentors:</label>
                    </div>
                    <div className="col-md-8">
                      <div className="d-flex" data-testid="mentor-field-wrapper">
                        <Field
                          className="form-control f"
                          type="text"
                          name="mentor"
                          list="mentors-list"
                        />
                        <datalist id="mentors-list">
                          {
                            mentors
                              .filter(({ id }) => !groupMentors.find((mentor) => mentor.id === id))
                              .map(({
                                id, firstName, lastName, email,
                              }) => <option key={id} value={`${firstName} ${lastName} ${email}`} />)
                          }
                        </datalist>
                        <Button
                          id="add-mentor-btn"
                          variant="info"
                          onClick={() => addMentor(values.mentor, () => setFieldValue('mentor', ''))}
                          disabled={!dirty}
                        >
                          +
                        </Button>
                      </div>
                      {mentorInputError && <p className="text-danger mb-0">{mentorInputError}</p>}
                      <div className="w-100">
                        <ul className="col-md-12 d-flex flex-wrap justify-content-between p-0">
                          {
                            groupMentors.map(({ id, firstName, lastName }) => (
                              <li
                                key={id}
                                id="chosenMentorName"
                                className={classNames(
                                  'd-flex bg-light border border-outline-secondary rounded',
                                  styles['datalist-item'],
                                )}
                              >
                                {firstName} {lastName}
                                <button
                                  type="button"
                                  className={classNames('btn p-0 ml-auto mr-2 font-weight-bold', styles.cross)}
                                  onClick={() => removeMentor(id)}
                                >
                                  X
                                </button>
                              </li>
                            ))
                          }
                        </ul>
                      </div>
                    </div>
                  </div>
                  <div className="row mb-3">
                    <div className="col d-flex align-items-start">
                      <label className="mt-2" htmlFor="finish-date">Students:</label>
                    </div>
                    <div className="col-md-8">
                      <div className="d-flex" data-testid="students-field-wrapper">
                        <Field
                          className="form-control f"
                          type="text"
                          name="student"
                          list="students-list"
                        />
                        <datalist id="students-list">
                          {
                            students
                              .filter(({ id }) => !groupStudents.find((mentor) => mentor.id === id))
                              .map(({
                                id, firstName, lastName, email,
                              }) => <option key={id} value={`${firstName} ${lastName} ${email}`} />)
                          }
                        </datalist>
                        <Button
                          id="add-student-btn"
                          variant="info"
                          onClick={() => addStudent(values.student, () => setFieldValue('student', ''))}
                          disabled={!dirty}
                        >
                          +
                        </Button>
                      </div>
                      {studentInputError && <p className="text-danger mb-0">{studentInputError}</p>}
                      <div className="w-100">
                        <ul className="col-12 d-flex flex-wrap justify-content-between p-0">
                          {
                            groupStudents.map(({ id, firstName, lastName }) => (
                              <li
                                key={id}
                                className={classNames(
                                  'd-flex bg-light border border-outline-secondary rounded',
                                  styles['datalist-item'],
                                )}
                              >
                                {firstName} {lastName}
                                <button
                                  type="button"
                                  className={classNames('btn p-0 ml-auto mr-2 font-weight-bold', styles.cross)}
                                  onClick={() => removeStudent(id)}
                                >
                                  X
                                </button>
                              </li>
                            ))
                          }
                        </ul>
                      </div>
                    </div>
                  </div>
                  <div className="row justify-content-around mt-4">
                    <Button type="reset" variant="secondary" className={classNames('w-25', styles['clear-button'])} disabled={ (!dirty &&  prevGroupMentors !== groupMentors && prevGroupStudents !== groupStudents) || isEditing} onClick={handleReset}>
                      Reset
                    </Button>
                    <Button id="submit" type="submit" className="btn btn-secondary w-25 buttonConfirm" disabled={!isValid || (!dirty &&  prevGroupMentors !== groupMentors && prevGroupStudents !== groupStudents) || isEditing}>
                      Confirm
                    </Button>
                  </div>
                </Form>
              )}
            </Formik>
          </WithLoading>
        </div>
      </div>
    </div>
  );
}