こんにちは!3月になって花粉症に苦しめられているむらです。
先日、プロジェクトの関係で海外出張に行ってきました。普段、海外のエンジニアの方達と開発を進めているのですがオンラインで仕事を進めていることもあり、どんな方達なのかお顔も人柄もよくわからないでいました。
プロジェクトにジョインして初めて実際にお会いするということもあり、今回はチームビルディングに重点を置いたワークショップを実施しました。
その中で日本チームとの文化的な価値観の違いを可視化するカルチャーマップワークショップを行いました。カルチャーマップにより仕事をする中でチームの文化を可視化し、大きく異なる価値観を持っている項目について議論し、より良いチームにするためにはどうすればよいのか考えることができます。
カルチャーマップの説明は以下をご参照ください。
今回の出張では、ホワイトボードツールを使ってメンバーの皆さんにアンケートを取りました。しかし、皆さんに答えていただいた結果から平均値を計算して、きれいな図に整形する必要があり少し大変な思いをしました。
そこで、これをウェブアプリにしてしまえば簡単に回答の収集と結果集計・視覚化ができるのではないかと思いつきました。
ということで、サクッとアプリを作ってみました!
用意した機能
このウェブアプリでは、以下のことができるように実装しました。
- カルチャーマップのデータを登録
- 登録されたデータをグラフで表示
例えば、チームメンバーにアンケートに答えてもらい、その結果を登録することで、チーム全体のカルチャーマップを可視化できます。
開発環境と技術スタック
今回の開発では、以下の技術スタックを使用しました。
フロントエンド: Next.js, Chart.js, Tailwind CSS
バックエンド: Supabase (PostgreSQL)
Supabaseは、GoogleのFirebaseのようなデータベースや認証機能などを簡単に利用できるサービスです。今回は、あまり使ったことがなかったこともあり、こちらをデータベースとして使用しました。様々な言語用にライブラリが用意されているので、APIキーなどの認証情報を用意すればすぐにデータベースと接続できました。
Chart.jsは、グラフ描画ライブラリで、カルチャーマップを視覚的に表示するために使用しました。
Supabaseの設定
Supabaseでは、PostgreSQLをGUIで操作でき、テーブル設計やデータ操作が簡単に行えました。
今回は、フロントで入力されたユーザ名と所属チーム(日本かオフショアか)、各質問の回答を1つのレコードとするテーブルを作りました。
Next.jsからは、SupabaseのJavaScriptクライアントライブラリを使用して、このテーブルに対してデータの取得や登録を行いました。
import { createClient } from "@supabase/supabase-js";
const supabaseUrl = "https:/###############.supabase.co";
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_KEY;
const supabase = createClient(supabaseUrl, supabaseKey);
const { data, error } = await supabase.from("results").select();
フロントエンド実装
各質問にスライダーを使って直感的に回答できるようにしてみました。
1~5の5段階評価で1刻みにしました。
<input
type="range"
min={MIN_VALUE}
max={MAX_VALUE}
value={value}
onChange={(e) => onChange(parseInt(e.target.value))}
className="w-full"
/>
データの取得には、useEffectフックを使用し、Supabaseから取得したデータをChart.jsに渡してグラフを描画しました。
ただ、Supabaseに登録されているレコードは一人ひとりの回答を示しているので、カルチャーマップの形にするためにはチームごとに各質問の平均値を計算する必要があります。
この計算処理を行い、Chart.jsに渡してグラフの描画を行いました。
また、グラフの左右にそれぞれの質問項目と評価の軸(「Aよりである」、「Bよりである」など)を表示させるために、chartOptions.y.ticks.callback
とchartOptions.y1.ticks.callback
にラベルを設定しました。
これによって左だけでなく右側にも任意のラベルを表示できるようになりました。
さらに、右側のラベルについてはy1.offset
をfalse
に設定しないと左側のラベルと少し位置がズレて表示されてしまいました。
const AverageLineChart = ({ averageData }) => {
const chartOptions = {
indexAxis: "y",
scales: {
x: {
min: 0,
max: 5,
},
y: {
beginAtZero: false,
position: "left",
ticks: {
callback: function (value, index, values) {
// 左側のy軸のラベル
return `[${label_text[index]}] ${indicator_text_left[index]}`;
},
},
},
y1: {
type: "category",
position: "right",
offset: false,
ticks: {
reverse: true,
callback: function (value, index, values) {
// 右側のy軸のラベル
return indicator_text_right[index];
},
},
grid: {
display: false,
},
},
},
plugins: {
title: {
display: true,
text: "チームごとの回答平均 (縦向き折れ線)",
},
},
};
return <Line data={chartData} options={chartOptions} />;
};
export default AverageLineChart;
※ここのデータは実際にワークしたときのデータではなく、適当に作ったサンプルデータです。
まとめ
Next.jsとSupabaseを使うことで、データベース連携が必要なリッチなウェブアプリを、短期間で開発できました。
特に、Supabaseは、データベースの知識が少なくても簡単に扱えるため、バックエンドに時間をかけずにフロントエンドの開発に集中できました。
さらに、大枠をGeminiに作ってもらうことでもっと開発時間を短縮できたと思います。
今回はとりあえずサクッと作りたかったので、最低限の機能で拡張性に欠ける実装になってしまいました。
作っている中で、やはりユーザ認証はほしいなあとかいろいろなチームが増えたときに対応できないなあ、などまだまだ発展させていきたいなと思いました。
またどこかのタイミングでカルチャーマップを作ることがあれば、このアプリを活用してみたいと思います。