알린홈마의 코드친구들
article thumbnail

이제 첫 리포트를 생성해보자~!!

 

작업일지 리포트는 테이블형태(엑셀)로 작성하고 엑셀과 csv 파일로 내보내고 불러오게 가능하도록 구상을 했다.

마침 테이블 형태 관련해서 좋은 글과 좋은 라이브러리를 찾게 되었다.

https://handsontable.com/docs/react-data-grid/

 

React Data Grid - Documentation | Handsontable

Handsontable documentation What is Handsontable? Handsontable (pronounced "hands-on-table") is a JavaScript data grid component that brings the well-known look and feel of spreadsheets to your application. Thousands of business apps depend on Handsontable

handsontable.com

https://bloodstrawberry.tistory.com/272

 

React Handsontable로 csv 편집기 만들기 (1)

프로젝트 전체 링크 현재 - (1) Project Setting 다음 - (2) Drag & Drop 깃허브에서 코드 확인하기 대기업 같은 경우 보안이 까다롭기 때문에 문서를 편집하다가 NASCA에 걸릴 때가 많다. 문제는 NASCA에 걸려

bloodstrawberry.tistory.com


handsontable

handsontable 이란 라이브러리는 잘 알려진 스프레드시트의 모양과 느낌을 애플리케이션에 제공하는 자바스크립트 데이터 그리드 구성 요소 라고 공식문서에서 설명한다

 

npm install handsontable @handsontable/react

으로 설치를 진행하고 

 

index.css에 handsontable 라이브러리 css 를 불러와저야 한다고 한다

import 'handsontable/dist/handsontable.full.min.css';

 

그리고 사용할 컴포넌트에서

import { HotTable } from '@handsontable/react';
<HotTable
  data={[
    ['', 'Tesla', 'Volvo', 'Toyota', 'Ford'],
    ['2019', 10, 11, 12, 13],
    ['2020', 20, 11, 14, 13],
    ['2021', 30, 15, 12, 13]
  ]}
  rowHeaders={true}
  colHeaders={true}
  height="auto"
  licenseKey="non-commercial-and-evaluation" // for non-commercial use only
/>

다양한 옵션들(이번 프로젝트에서 사용한 옵션들)

 

더보기
  1. id : 테이블을 불러오기 위해 id를 지정해주어야 한다. hot 으로 고정
    ex) id="hot" 

  2. data: 테이블에 표시할 데이터를 넣어야 한다. 
    ex) data={data && data}  || 데이터를 넣어주지 않으면 기본 테이블이 나온다

  3. colHeaders: 기본 열 머리글(A, B, C)을 사용하거나 배열 또는 함수에서 제공하는 사용자 지정 값으로 설정
    ex) colHeaders={colHeaders}  || colHeaders?: boolean | string[] | ((index: number) => string);

  4. rowHeaders: 기본 행 헤더(1, 2, 3)를 사용하거나 배열 또는 함수에서 제공하는 사용자 지정 값으로 설정합니다.
    ex) rowHeaders={true}  || rowHeaders?: boolean | string[] | ((index: number) => string);

  5. manualColumnMove: 컬럼을 이동 설정
    ex) manualColumnMove={true} 

  6. fixedColumnsStart: 지정된 열의 위치를 지정하면 스크롤할 때 해당 열이 계속 표시
    ex) fixedColumnsStart={window.innerWidth > 1024 ? 1 : 0} 

  7. htCenter / htMiddle: 셀 텍스트 정렬 관련 설정 (중앙정렬 / 세로 중앙정렬) 
    ex) className="htCenter htMiddle z-0" colWidths={colWidths()} 

  8. rowHeights: 셀 높이 지정
    ex) rowHeights={`${(window.innerHeight - 200) / 10}`} 

  9. readOnly: true 면 편집이 불가능한 모드로 설정
    ex) readOnly={readOnly ? readOnly : false} 
  10. contextMenu: 컨텍스트 메뉴를 생성. 그리드의 다른 기능 중 원하는 위치에 새 행이나 열을 만들 수 있다.
    ex) contextMenu={true} 

  11. manualColumnResize: 컬럼 너비를 수정하도록 설정
    ex) manualColumnResize={true} 

  12. dropdownMenu: 드롭다운 메뉴 설정
    ex) dropdownMenu={true} 

  13. columnSorting: 컬럼당 정렬기능을 설정
    columnSorting={true} 

  14. afterColumnSort : 열을 정렬한 후 ColumnSorting 및 MultiColumnSorting 플러그인에서 발생하는 훅, 함수를 전달
    afterColumnSort={exclude}

handsontable에서 데이터 추출해서 파이어스토어로 넘기기

파이어베이스 데이터 저장 API 짜기

파이어스토어에서 데이터를 추가하는 방식 setDoc() vs addDoc()

setDoc() 

단일 문서 생성 및 덮어쓰기는 setDoc()메서드를 사용한다. 문서가 없으면 생성하고 문서가 있으면 새로 제공한 데이터로 내용을 덮어쓴다. 단 setDoc()을 사용하게 되면  문서의 ID를 지정을 해줘야 한다

 

이번 프로젝트에서 사용한 문서 ID 생성 라이브러리

더보기

reportId는 id를 생성해주는 uuid.js 라이브러리를 사용

https://www.npmjs.com/package/uuid

 

uuid

RFC4122 (v1, v4, and v5) UUIDs. Latest version: 9.0.0, last published: a year ago. Start using uuid in your project by running `npm i uuid`. There are 55695 other projects in the npm registry using uuid.

www.npmjs.com

 

출처: https://www.npmjs.com/package/uuid

타임스탬프를 기준으로 uuid를 생성해주는 v1을 사용하였다.

import { v1 as uuidv1 } from "uuid";

export async function addReport(data) {
  const reportId = uuidv1();
  await setDoc(doc(db, "reports", reportId), data);
}

 

 

addDoc()

문서에 유의미한 ID를 두지 않고 Cloud Firestore에서 자동으로 ID를 생성하도록 할때 사용하는 addDoc()을 사용한다

 

이번 프로젝트에서는 

문서 정보에 reportId 값을 따로 저장해주고 싶어 setDoc() 을 사용하였다.

또한, 작성과 동시에 작성된 페이지로 이동해주기 위해 reportId 를 차가로 반환해주었다

 

firestore.js

export async function addReport(headers, data, user, title) {
  const reportId = uuidv1();
  await setDoc(doc(db, "reports", reportId), {
    headers: headers,
    data: data,
    userId: user.uid,
    createdAt: serverTimestamp(),
    title: title,
    reportId: reportId,
    fix: false,
    writer: user.displayName,
  });

  return reportId;
}

 

사용예시

 

await addReport(headers, data, user, title).then((reportId) => {
      navigate(`/reports/${reportId}`);
    });

handontalbe 에서 데이터 추출

https://handsontable.com/docs/react-data-grid/saving-data/

 

Saving data - React Data Grid | Handsontable

Saving data Save data after each change to the data set, using Handsontable's API hooks. Preserve the table's state by saving data to the local storage. Save changes using a callback To track changes made in your data grid, use Handsontable's afterChange h

handsontable.com

 

writePage.jsx

  import React, { useRef } from "react";
  import { registerAllModules } from "handsontable/registry";
  //.
  //.
  //.
  registerAllModules();
  const hotRef = useRef(null);
  //.
  //.
  //.
  const saveClickCallback = async (e) => {
    e.preventDefault();
    hot = hotRef.current.hotInstance;
    let data = JSON.stringify(hot.getData());
    await addReport(data).then(() => {
      navigate("/");
    });
  };
  
  return (
    <div className="w-full">
      <form onSubmit={(...arg) => saveClickCallback(...arg)}>
    	<HotTableOption colHeaders={true} data={data} hotRef={hotRef} />
      </form>
    </div>
  );

데이터는 getData()함수로 배열 형태로 받아오고

JSON.stringify() 를 통해 JSON 문자열로 변환 해주어 기존에 만든 addReport()에 전달해준다

 

firebase 콘솔에서 확인된 JSON 데이터 

 

 

HotTable은 총 3곳이상의 페이지에서 사용될 예정이라 컴포넌트화 해서 빼두었다

더보기

 

import { HotTable } from "@handsontable/react";
import React from "react";

export default function HotTableOption({tableData, data,hotRef,colHeaders, readOnly}) {
  
  // 첫번째 열은 sort 되지 않도록 하는 함수
  const exclude = () => {
    const handsontableInstance = hotRef.current.hotInstance;
    const lastRowIndex = handsontableInstance.countRows() - 1;
    
    handsontableInstance.rowIndexMapper.moveIndexes(
      handsontableInstance.toVisualRow(0),
      0
    );
    
    handsontableInstance.rowIndexMapper.moveIndexes(
      handsontableInstance.toVisualRow(lastRowIndex),
      lastRowIndex
    );
  };

  // colWidth를 반환하는 함수
  const colWidths = () => {
    let colWidths = [];
    if (tableData) {
      if (window.innerWidth < 1024) {
        for (let i = 0; i < JSON.parse(tableData.data)[0].length; i++) {
          colWidths.push(100);
        }
        return colWidths;
      }
      for (let i = 0; i < JSON.parse(tableData.data)[0].length; i++) {
        colWidths.push(
          window.innerWidth / JSON.parse(tableData.data)[0].length
        );
      }
      return colWidths;
    } else {
      return (colWidths =
        window.innerWidth > 1024
          ? (window.innerWidth - 300) / 5
          : window.innerWidth / 5);
    }
  };

  return (
    <HotTable
      id="hot"
      data={data && data}
      colHeaders={colHeaders}
      rowHeaders={true}
      manualColumnMove={true}
      fixedColumnsStart={window.innerWidth > 1024 ? 1 : 0}
      className="htCenter htMiddle z-0"
      colWidths={colWidths()}
      rowHeights={`${(window.innerHeight - 200) / 10}`}
      licenseKey="non-commercial-and-evaluation"
      readOnly={readOnly ? readOnly : false}
      ref={hotRef}
      contextMenu={true}
      manualColumnResize={true}
      dropdownMenu={true}
      columnSorting={true}
      width={"100%"}
      afterColumnSort={exclude}
      // for non-commercial use only
    />
  );
}
profile

알린홈마의 코드친구들

@알린팬클럽홈마

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

profile on loading

Loading...