A-yum1 commited on
Commit
3b67cd9
·
1 Parent(s): c8c2f4b

suwabe/databaseを反映

Browse files
templates/index.html CHANGED
@@ -1,8 +1,8 @@
1
  <!DOCTYPE html>
2
  <html lang="ja">
3
  <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>Voice Recorder Interface</title>
7
  <style>
8
  body {
@@ -19,12 +19,14 @@
19
  .chart {
20
  width: 300px;
21
  height: 300px;
22
- margin-bottom: 50px; /* 円グラフとボタンの間隔を狭く */
 
 
 
 
 
23
  }
24
-
25
  .record-button {
26
- position: fixed;
27
- bottom: 30px;
28
  width: 80px;
29
  height: 80px;
30
  background-color: transparent;
@@ -52,13 +54,10 @@
52
  border-radius: 10%;
53
  }
54
 
55
- .result-buttons {
56
- margin-top: 5px; /* ボタン間の距離を少し縮める */
57
- display: flex;
58
- gap: 10px;
59
- }
60
-
61
  .result-button {
 
 
 
62
  padding: 10px 20px;
63
  background-color: #4caf50;
64
  border: none;
@@ -66,9 +65,10 @@
66
  color: white;
67
  cursor: pointer;
68
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.4);
69
- transition: background-color 0.2s ease;
70
  }
71
-
 
 
72
  .result-button:hover {
73
  background-color: #388e3c;
74
  }
@@ -85,8 +85,12 @@
85
  </button>
86
 
87
  <div class="result-buttons">
88
- <button class="result-button" id="historyButton" onclick="showHistory()">会話履歴を表示</button>
89
- <button class="result-button" id="feedbackButton" onclick="showResults()">フィードバック画面を表示</button>
 
 
 
 
90
  </div>
91
 
92
  <script>
@@ -96,41 +100,45 @@
96
  let recordingInterval;
97
 
98
  // Chart.js の初期化
99
- const ctx = document.getElementById('speechChart').getContext('2d');
100
  const speechChart = new Chart(ctx, {
101
- type: 'doughnut',
102
  data: {
103
- labels: ['自分', '他の人'],
104
- datasets: [{
105
- data: [30, 70],
106
- backgroundColor: ['#4caf50', '#757575'],
107
- }],
 
 
108
  },
109
  options: {
110
  responsive: true,
111
  plugins: {
112
  legend: {
113
  display: true,
114
- position: 'bottom',
115
- labels: { color: 'white' }
116
- }
117
- }
118
- }
119
  });
120
 
121
  async function toggleRecording() {
122
- const recordButton = document.getElementById('recordButton');
123
 
124
  if (!isRecording) {
125
  // 録音開始
126
  isRecording = true;
127
- recordButton.classList.add('recording');
128
  try {
129
- const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
 
 
130
  mediaRecorder = new MediaRecorder(stream);
131
  audioChunks = [];
132
 
133
- mediaRecorder.ondataavailable = event => {
134
  if (event.data.size > 0) {
135
  audioChunks.push(event.data);
136
  }
@@ -149,14 +157,14 @@
149
  }
150
  }, 10000);
151
  } catch (error) {
152
- console.error('マイクへのアクセスに失敗しました:', error);
153
  isRecording = false;
154
- recordButton.classList.remove('recording');
155
  }
156
  } else {
157
  // 手動停止
158
  isRecording = false;
159
- recordButton.classList.remove('recording');
160
  clearInterval(recordingInterval);
161
  if (mediaRecorder && mediaRecorder.state === "recording") {
162
  mediaRecorder.stop();
@@ -165,41 +173,58 @@
165
  }
166
 
167
  function sendAudioChunks(chunks) {
168
- const audioBlob = new Blob(chunks, { type: 'audio/wav' });
169
  const reader = new FileReader();
170
  reader.onloadend = () => {
171
- const base64String = reader.result.split(',')[1]; // Base64エンコードされた音声データ
172
- fetch('/upload_audio', {
173
- method: 'POST',
174
  headers: {
175
- 'Content-Type': 'application/json',
176
  },
177
  body: JSON.stringify({ audio_data: base64String }),
178
  })
179
- .then(response => response.json())
180
- .then(data => {
181
- if (data.error) {
182
- alert('エラー: ' + data.error);
183
- console.error(data.details);
184
- } else if (data.rate !== undefined) {
185
- // 解析結果が返ってきた場合はチャートを更新
186
- speechChart.data.datasets[0].data = [data.rate, 100 - data.rate];
187
- speechChart.update();
188
- alert('音声の解析が完了しました。自分の音声: ' + data.rate.toFixed(2) + '%, 他の人: ' + (100 - data.rate).toFixed(2) + '%');
189
- } else {
190
- alert('音声がバックエンドに送信されました。');
191
- }
192
- // 録音が継続中であれば、再度録音を開始(自動録音の連続処理)
193
- if (isRecording && mediaRecorder && mediaRecorder.state === "inactive") {
194
- mediaRecorder.start();
195
- }
196
- })
197
- .catch(error => {
198
- console.error('エラー:', error);
199
- if (isRecording && mediaRecorder && mediaRecorder.state === "inactive") {
200
- mediaRecorder.start();
201
- }
202
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
  };
204
  reader.readAsDataURL(audioBlob);
205
  }
@@ -207,13 +232,13 @@
207
  function showHistory() {
208
  // 会話履歴表示の画面があれば、そのページへ遷移する例
209
  // window.location.href = 'history';
210
- alert('会話履歴を表示する機能は未実装です。');
211
  }
212
 
213
  function showResults() {
214
  // フィードバック画面へ遷移
215
- window.location.href = 'feedback';
216
  }
217
  </script>
218
  </body>
219
- </html>
 
1
  <!DOCTYPE html>
2
  <html lang="ja">
3
  <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
  <title>Voice Recorder Interface</title>
7
  <style>
8
  body {
 
19
  .chart {
20
  width: 300px;
21
  height: 300px;
22
+ margin-bottom: 20px; /* 円グラフとボタンの間隔を狭く */
23
+ }
24
+ .controls {
25
+ display: flex;
26
+ flex-direction: column;
27
+ align-items: center;
28
  }
 
29
  .record-button {
 
 
30
  width: 80px;
31
  height: 80px;
32
  background-color: transparent;
 
54
  border-radius: 10%;
55
  }
56
 
 
 
 
 
 
 
57
  .result-button {
58
+ margin-left: 10px;
59
+
60
+ margin-top: 20px;
61
  padding: 10px 20px;
62
  background-color: #4caf50;
63
  border: none;
 
65
  color: white;
66
  cursor: pointer;
67
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.4);
 
68
  }
69
+ .result {
70
+ display: flex;
71
+ }
72
  .result-button:hover {
73
  background-color: #388e3c;
74
  }
 
85
  </button>
86
 
87
  <div class="result-buttons">
88
+ <button class="result-button" id="historyButton" onclick="showHistory()">
89
+ 会話履歴を表示
90
+ </button>
91
+ <button class="result-button" id="feedbackButton" onclick="showResults()">
92
+ フィードバック画面を表示
93
+ </button>
94
  </div>
95
 
96
  <script>
 
100
  let recordingInterval;
101
 
102
  // Chart.js の初期化
103
+ const ctx = document.getElementById("speechChart").getContext("2d");
104
  const speechChart = new Chart(ctx, {
105
+ type: "doughnut",
106
  data: {
107
+ labels: ["自分", "他の人"],
108
+ datasets: [
109
+ {
110
+ data: [30, 70],
111
+ backgroundColor: ["#4caf50", "#757575"],
112
+ },
113
+ ],
114
  },
115
  options: {
116
  responsive: true,
117
  plugins: {
118
  legend: {
119
  display: true,
120
+ position: "bottom",
121
+ labels: { color: "white" },
122
+ },
123
+ },
124
+ },
125
  });
126
 
127
  async function toggleRecording() {
128
+ const recordButton = document.getElementById("recordButton");
129
 
130
  if (!isRecording) {
131
  // 録音開始
132
  isRecording = true;
133
+ recordButton.classList.add("recording");
134
  try {
135
+ const stream = await navigator.mediaDevices.getUserMedia({
136
+ audio: true,
137
+ });
138
  mediaRecorder = new MediaRecorder(stream);
139
  audioChunks = [];
140
 
141
+ mediaRecorder.ondataavailable = (event) => {
142
  if (event.data.size > 0) {
143
  audioChunks.push(event.data);
144
  }
 
157
  }
158
  }, 10000);
159
  } catch (error) {
160
+ console.error("マイクへのアクセスに失敗しました:", error);
161
  isRecording = false;
162
+ recordButton.classList.remove("recording");
163
  }
164
  } else {
165
  // 手動停止
166
  isRecording = false;
167
+ recordButton.classList.remove("recording");
168
  clearInterval(recordingInterval);
169
  if (mediaRecorder && mediaRecorder.state === "recording") {
170
  mediaRecorder.stop();
 
173
  }
174
 
175
  function sendAudioChunks(chunks) {
176
+ const audioBlob = new Blob(chunks, { type: "audio/wav" });
177
  const reader = new FileReader();
178
  reader.onloadend = () => {
179
+ const base64String = reader.result.split(",")[1]; // Base64エンコードされた音声データ
180
+ fetch("/upload_audio", {
181
+ method: "POST",
182
  headers: {
183
+ "Content-Type": "application/json",
184
  },
185
  body: JSON.stringify({ audio_data: base64String }),
186
  })
187
+ .then((response) => response.json())
188
+ .then((data) => {
189
+ if (data.error) {
190
+ alert("エラー: " + data.error);
191
+ console.error(data.details);
192
+ } else if (data.rate !== undefined) {
193
+ // 解析結果が返ってきた場合はチャートを更新
194
+ speechChart.data.datasets[0].data = [
195
+ data.rate,
196
+ 100 - data.rate,
197
+ ];
198
+ speechChart.update();
199
+ alert(
200
+ "音声の解析が完了しました。自分の音声: " +
201
+ data.rate.toFixed(2) +
202
+ "%, 他の人: " +
203
+ (100 - data.rate).toFixed(2) +
204
+ "%"
205
+ );
206
+ } else {
207
+ alert("音声がバックエンドに送信されました。");
208
+ }
209
+ // 録音が継続中であれば、再度録音を開始(自動録音の連続処理)
210
+ if (
211
+ isRecording &&
212
+ mediaRecorder &&
213
+ mediaRecorder.state === "inactive"
214
+ ) {
215
+ mediaRecorder.start();
216
+ }
217
+ })
218
+ .catch((error) => {
219
+ console.error("エラー:", error);
220
+ if (
221
+ isRecording &&
222
+ mediaRecorder &&
223
+ mediaRecorder.state === "inactive"
224
+ ) {
225
+ mediaRecorder.start();
226
+ }
227
+ });
228
  };
229
  reader.readAsDataURL(audioBlob);
230
  }
 
232
  function showHistory() {
233
  // 会話履歴表示の画面があれば、そのページへ遷移する例
234
  // window.location.href = 'history';
235
+ alert("会話履歴を表示する機能は未実装です。");
236
  }
237
 
238
  function showResults() {
239
  // フィードバック画面へ遷移
240
+ window.location.href = "feedback";
241
  }
242
  </script>
243
  </body>
244
+ </html>
templates/new_person.html ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="ja">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <link rel="stylesheet" href="style.css" />
7
+ <title>ユーザー登録画面</title>
8
+ </head>
9
+ <body>
10
+ <form method="POST">
11
+ <div class="container">
12
+ <h2>ユーザー登録</h2>
13
+ <div class="flex">
14
+ <div>
15
+ <label for="">ユーザー名</label>
16
+ <br />
17
+ <input
18
+ type="text"
19
+ id="username"
20
+ name="username"
21
+ placeholder="例:技育 太郎"
22
+ required
23
+ oninput="validateForm()"
24
+ />
25
+ <br />
26
+ <br />
27
+ <label for="">パスワード</label>
28
+ <br />
29
+ <input
30
+ type="password"
31
+ id="password"
32
+ name="password"
33
+ placeholder="半角数字8文字以上20文字以内"
34
+ maxlength="20"
35
+ required
36
+ oninput="validateForm()"
37
+ />
38
+ <br />
39
+ <br />
40
+ <label for=""> パスワード再入力</label>
41
+ <br />
42
+ <input
43
+ type="password"
44
+ id="repassword"
45
+ name="repassword"
46
+ placeholder="再入力してください"
47
+ maxlength="20"
48
+ required
49
+ oninput="validateForm()"
50
+ />
51
+ <p id="passwordError" class="error-message"></p>
52
+ <br />
53
+ </div>
54
+ <div class="new-person-right-container">
55
+ <label for="">
56
+ 録音ボタンを押した後、10秒間声を録音してください</label
57
+ >
58
+ <br />
59
+ <p class="record-p">
60
+ 例文:昔々、あるところにおばあさんとおじいさんがいました。<br />
61
+ おばあさんは川に洗濯に、おじいさんは山に芝刈りに行きました。
62
+ </p>
63
+ <div class="record-container">
64
+ <button
65
+ class="record-button"
66
+ id="recordButton"
67
+ onclick="toggleRecording()"
68
+ >
69
+ <div class="record-icon" id="recordIcon"></div>
70
+ </button>
71
+ </div>
72
+
73
+ <p id="countdownDisplay"></p>
74
+ </div>
75
+ </div>
76
+
77
+ <div class="flex">
78
+ <input
79
+ type="submit"
80
+ value="会員登録を行う"
81
+ name="submit"
82
+ id="submit"
83
+ class="history-button disabled"
84
+ disabled
85
+ />
86
+ </div>
87
+ </div>
88
+ </form>
89
+ <script>
90
+ let mediaRecorder;
91
+ let audioChunks = [];
92
+ let countdownTimer;
93
+ let isAudioRecorded = false;
94
+
95
+ async function toggleRecording() {
96
+ const recordButton = document.getElementById("recordButton");
97
+
98
+ const countdownDisplay = document.getElementById("countdownDisplay");
99
+
100
+ recordButton.disabled = true; // ボタンを無効化(連打防止)
101
+ countdownDisplay.textContent = "録音中…"; // 初期表示
102
+ isAudioRecorded = false;
103
+ recordIcon.classList.add("recording"); // アイコンを正方形に変更
104
+
105
+ try {
106
+ const stream = await navigator.mediaDevices.getUserMedia({
107
+ audio: true,
108
+ });
109
+ mediaRecorder = new MediaRecorder(stream);
110
+ audioChunks = [];
111
+
112
+ mediaRecorder.ondataavailable = (event) => {
113
+ if (event.data.size > 0) {
114
+ audioChunks.push(event.data);
115
+ }
116
+ };
117
+
118
+ mediaRecorder.onstop = () => {
119
+ sendAudioChunks([...audioChunks]); // 録音データをサーバーに送信
120
+ audioChunks = [];
121
+ recordButton.disabled = false; // ボタンを再度有効化
122
+ countdownDisplay.textContent = ""; // カウントダウンを消す
123
+ isAudioRecorded = true; // 録音完了
124
+ recordIcon.classList.remove("recording"); // アイコンを元の丸に戻す
125
+ validateForm(); // フォームの状態を更新
126
+ };
127
+
128
+ mediaRecorder.start();
129
+ startCountdown(10, countdownDisplay); // 10秒のカウントダウン開始
130
+
131
+ // 10秒後に録音を自動停止
132
+ setTimeout(() => {
133
+ if (mediaRecorder.state === "recording") {
134
+ mediaRecorder.stop();
135
+ }
136
+ }, 10000);
137
+ } catch (error) {
138
+ console.error("マイクのアクセスに失敗しました:", error);
139
+ recordButton.disabled = false; // ボタンを再度有効化
140
+ countdownDisplay.textContent = ""; // カウントダウンを消す
141
+ recordIcon.classList.remove("recording"); // アイコンを元の丸に戻す
142
+ }
143
+ }
144
+
145
+ function startCountdown(seconds, displayElement) {
146
+ let remaining = seconds;
147
+ displayElement.textContent = `録音終了まで: ${remaining}秒`;
148
+
149
+ countdownTimer = setInterval(() => {
150
+ remaining--;
151
+ displayElement.textContent = `録音終了まで: ${remaining}秒`;
152
+
153
+ if (remaining <= 0) {
154
+ clearInterval(countdownTimer); // カウントダウン終了
155
+ }
156
+ }, 1000);
157
+ }
158
+
159
+ function sendAudioChunks(chunks) {
160
+ const audioBlob = new Blob(chunks, { type: "audio/wav" });
161
+ const reader = new FileReader();
162
+ reader.onloadend = () => {
163
+ const base64String = reader.result.split(",")[1]; // Base64 に変換
164
+ fetch("/upload_audio", {
165
+ method: "POST",
166
+ headers: {
167
+ "Content-Type": "application/json",
168
+ },
169
+ body: JSON.stringify({ audio_data: base64String }),
170
+ })
171
+ .then((response) => response.json())
172
+ .then((data) => {
173
+ alert("音声がサーバーに送信されました。");
174
+ })
175
+ .catch((error) => {
176
+ console.error("送信エラー:", error);
177
+ });
178
+ };
179
+ reader.readAsDataURL(audioBlob);
180
+ }
181
+
182
+ // フォームの入力状態をチェック
183
+ function validateForm() {
184
+ const username = document.getElementById("username").value.trim();
185
+ const password = document.getElementById("password").value.trim();
186
+ const repassword = document.getElementById("repassword").value.trim();
187
+ const submitButton = document.getElementById("submit");
188
+ const passwordError = document.getElementById("passwordError");
189
+
190
+ if (password !== repassword) {
191
+ passwordError.textContent = "パスワードが一致しません。";
192
+ submitButton.classList.add("disabled");
193
+ submitButton.disabled = true;
194
+ return;
195
+ } else {
196
+ passwordError.textContent = "";
197
+ }
198
+
199
+ if (
200
+ username !== "" &&
201
+ password.length >= 8 &&
202
+ password.length <= 20 &&
203
+ password === repassword &&
204
+ isAudioRecorded
205
+ ) {
206
+ submitButton.classList.remove("disabled");
207
+ submitButton.disabled = false;
208
+ } else {
209
+ submitButton.classList.add("disabled");
210
+ submitButton.disabled = true;
211
+ }
212
+ }
213
+ </script>
214
+ </body>
215
+ </html>
templates/resetting_password.html ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="ja">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>パスワード再設定画面</title>
7
+ <link rel="stylesheet" href="style.css">
8
+
9
+ </head>
10
+ <body>
11
+ <form method="POST">
12
+ <div class="container">
13
+ <h2>パスワード再設定</h2>
14
+ <div>
15
+ <label for="">ユーザ名</label>
16
+ <input type="text" name="username" />
17
+ <br />
18
+ <br />
19
+ <label for="">パスワード</label>
20
+ <input type="password" name="password" />
21
+ <br />
22
+ </div>
23
+ <div class="flex">
24
+ <input
25
+ type="submit"
26
+ class="history-button"
27
+ id="loginButton"
28
+ onclick="showRecorder()"
29
+ value="パスワード再設定"
30
+ />
31
+ </div>
32
+ </form>
33
+
34
+ </div>
35
+
36
+
37
+
38
+ <script>
39
+ // 会話データを表示
40
+ async function displayTranscription() {
41
+ const transcriptionElement = document.getElementById("transcription");
42
+
43
+ try {
44
+ // バックエンドからデータを取得(デモ用のURLを指定)
45
+ const response = await fetch("/api/transcription");
46
+ if (!response.ok) throw new Error("データ取得に失敗しました。");
47
+
48
+ const data = await response.json();
49
+
50
+ // 会話内容を整形して表示
51
+ const formattedText = data.conversations
52
+ .map((conv, index) => `【${conv.speaker}】 ${conv.text}`)
53
+ .join("\n");
54
+
55
+ transcriptionElement.textContent = formattedText;
56
+ } catch (error) {
57
+ transcriptionElement.textContent = `エラー: ${error.message}`;
58
+ console.error("データ取得エラー:", error);
59
+ }
60
+ }
61
+
62
+ // 初期化処理
63
+ displayTranscription();
64
+
65
+ //画面遷移
66
+ function showRecorder() {
67
+ // 録音画面へ遷移
68
+ window.location.href = "/index";
69
+ }
70
+ function showFeedback() {
71
+ // フィードバック画面へ遷移
72
+ window.location.href = "/feedback";
73
+ }
74
+ </script>
75
+ </body>
76
+ </html>
templates/style.css ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @charset "UTF-8";
2
+ body {
3
+ font-family: Arial, sans-serif;
4
+ padding: 20px;
5
+ background-color: #f4f4f4;
6
+ height: 100vh;
7
+ display: flex;
8
+ justify-content: center;
9
+ align-items: center;
10
+ }
11
+
12
+ h2 {
13
+ margin-bottom: 20px;
14
+ text-align: center;
15
+ }
16
+
17
+ a {
18
+ text-decoration: none;
19
+ color: #000000cc;
20
+ }
21
+ a:hover {
22
+ text-decoration: underline;
23
+ }
24
+ .container {
25
+ max-width: 800px;
26
+
27
+ background-color: #fff;
28
+ padding: 20px 80px;
29
+ border-radius: 8px;
30
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
31
+ }
32
+
33
+ #transcription {
34
+ white-space: pre-wrap;
35
+ padding: 10px;
36
+ background-color: #e9e9e9;
37
+ border-radius: 4px;
38
+ margin-bottom: 20px;
39
+ max-height: 400px;
40
+ overflow-y: auto;
41
+ }
42
+ button {
43
+ margin: 5px;
44
+ padding: 10px 10px;
45
+ border: none;
46
+ border-radius: 4px;
47
+ background-color: #007bff;
48
+ color: #fff;
49
+ cursor: pointer;
50
+ }
51
+ .history-button {
52
+ margin-top: 20px;
53
+
54
+ padding: 10px 20px;
55
+ background-color: #007bff;
56
+ color: white;
57
+ border: none;
58
+ border-radius: 5px;
59
+ cursor: pointer;
60
+ }
61
+ history-button:hover {
62
+ background-color: #0056b3;
63
+ }
64
+
65
+ .flex {
66
+ display: flex;
67
+ justify-content: center;
68
+ }
69
+ .new-person {
70
+ text-align: center;
71
+ }
72
+
73
+ .controls {
74
+ display: flex;
75
+ flex-direction: column;
76
+ align-items: center;
77
+ }
78
+ .record-button {
79
+ width: 80px;
80
+ height: 80px;
81
+ background-color: transparent;
82
+ border-radius: 50%;
83
+
84
+ display: flex;
85
+ justify-content: center;
86
+ align-items: center;
87
+ cursor: pointer;
88
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.4);
89
+ transition: all 0.2s ease;
90
+ }
91
+
92
+ .record-icon {
93
+ width: 60px;
94
+ height: 60px;
95
+ background-color: #d32f2f;
96
+ border-radius: 50%;
97
+ transition: all 0.2s ease;
98
+ }
99
+
100
+ .recording .record-icon {
101
+ width: 40px;
102
+ height: 40px;
103
+ border-radius: 10%;
104
+ }
105
+
106
+ .record-p {
107
+ border: 2px dashed #0000008c;
108
+ }
109
+
110
+ .disabled {
111
+ background-color: gray;
112
+ cursor: not-allowed;
113
+ }
114
+
115
+ .record-icon.recording {
116
+ width: 40px;
117
+ height: 40px;
118
+ border-radius: 0;
119
+ }
120
+
121
+ .new-person-right-container {
122
+ padding-left: 20px;
123
+ }
124
+
125
+ .record-container {
126
+ display: flex;
127
+ justify-content: center;
128
+ }