Merge pull request 'Sprint-3/MS-23' (#32) from Sprint-3/MS-23 into master

Reviewed-on: #32
This commit is contained in:
joseph 2024-09-18 17:59:33 +10:00
commit 84a636ea22
6 changed files with 264 additions and 58 deletions

View File

@ -12,6 +12,7 @@ use App\Traits\HasSearchRequest;
use Carbon\Carbon;
use Carbon\CarbonPeriod;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Modules\Admin\app\Models\Criteria;
use Modules\Admin\app\Models\Sprint;
use Modules\Admin\app\Models\SprintCriteria;
@ -46,6 +47,7 @@ class CriteriasController extends Controller
*/
public function getCriteriasForSprint($sprintId)
{
$userInfo = auth('admins')->user();
$sprint = Sprint::with('criterias')->find($sprintId);
if (!$sprint) {
$allUser = self::getAllUserJira();
@ -78,7 +80,7 @@ class CriteriasController extends Controller
}, $filteredObjects);
// Get criteria for sprint and member
$criterias = Criteria::where('type', "SPRINT")->get();
$criteriasSprint = Criteria::where('type', "SPRINT")->get()->toArray();
$members = Criteria::where('type', "MEMBER")->get()->toArray();
// Merge user information with criteria
@ -90,7 +92,7 @@ class CriteriasController extends Controller
}
// Map result to final format
$users = array_map(function ($item) {
$users = array_map(function ($item) use ($userInfo) {
return [
"user_id" => $item['accountId'],
"user" => $item['displayName'],
@ -98,21 +100,50 @@ class CriteriasController extends Controller
"criteria_id" => $item['id'],
"point" => "",
"note" => "",
"created_by" => "",
"created_by" => $userInfo->name,
"name" => $item['name'],
"description" => $item['description'],
"criteria_description" => $item['description'],
];
}, $result);
// Prepare final data
$criterias = array_map(function ($item) {
$item["expect"] = "";
$item["actual"] = "";
$item["note"] = "";
$item["point"] = "";
return $item;
}, $criteriasSprint);
$data = [
"criterias" => $criterias,
"users" => $users
];
} else {
$users = $sprint->users()->get();
$data = $sprint;
$data->users = $users;
// echo json_encode($sprint);
// dd();
$criterias = array_map(function ($item) {
$item["expect"] = $item["pivot"]["expect_result"];
$item["actual"] = $item["pivot"]["actual_result"];
$item["note"] = $item["pivot"]["note"];
$item["point"] = $item["pivot"]["point"];
unset($item["pivot"]); // Use unset() instead of delete
return $item;
}, $sprint->criterias->toArray());
$data = [
"id" => $sprint->id,
"name" => $sprint->name,
"point" => $sprint->point,
"project_id" => $sprint->project_id,
"start_date" => $sprint->start_date,
"end_date" => $sprint->end_date,
"complete_date" => $sprint->complete_date,
"criterias" => $criterias,
"users" => $users
];
}
return AbstractController::ResultSuccess($data);
}
@ -204,7 +235,7 @@ class CriteriasController extends Controller
* - user_note (optional): The note of the user (default is the same as note)
* @return bool True if successful, false otherwise
*/
public function updateCriteriasForSprint($sprintId, Request $request)
public function updateCriteriasForSprint2($sprintId, Request $request)
{
$user = auth('admins')->user();
@ -265,4 +296,77 @@ class CriteriasController extends Controller
return AbstractController::ResultSuccess("");
}
public function updateCriteriasForSprint($sprintId, Request $request)
{
// Validate dữ liệu đầu vào
$validated = $request->validate([
'name' => 'required|string',
'finalPoint' => 'required|string',
'project_id' => 'required|integer',
'criterias' => 'required|array',
'users' => 'required|array',
]);
// $sprintId = (int)$sprintId;
// Sử dụng transaction để đảm bảo tính toàn vẹn dữ liệu
DB::beginTransaction();
try {
// Kiểm tra xem Sprint có tồn tại không
$sprint = Sprint::find($sprintId);
if ($sprint) {
// Nếu tồn tại, xóa các liên kết hiện tại
$sprint->sprintsCriterias()->delete();
$sprint->usersCriterias()->delete();
// Cập nhật thông tin Sprint nếu cần
$sprint->update([
'name' => $validated['name'],
'project_id' => $validated['project_id'],
'point' => $validated['finalPoint'] ?? "",
// Cập nhật các trường khác nếu có
]);
} else {
// Nếu không tồn tại, tạo mới Sprint
$sprint = Sprint::create([
'id' => $sprintId,
'name' => $validated['name'],
'project_id' => $validated['project_id'],
'point' => $validated['finalPoint'] ?? "",
// Thêm các trường khác nếu có
]);
}
// Thêm lại các liên kết sprints_criterias
foreach ($validated['criterias'] as $criteria) {
SprintCriteria::create([
'sprint_id' => $sprint->id,
'criteria_id' => $criteria['id'],
'point' => $criteria['point'] ?? "",
'expect_result' => $criteria['expect'] ?? "",
'actual_result' => $criteria['actual'] ?? "",
'note' => $criteria['note'] ?? "",
]);
}
// Thêm lại các liên kết users_criterias
foreach ($validated['users'] as $userCriteria) {
UserCriteria::create([
'user_email' => $userCriteria['user_email'],
'criteria_id' => $userCriteria['criteria_id'],
'sprint_id' => $sprint->id,
'point' => $userCriteria['point'] ?? "",
'note' => $userCriteria['note'] ?? "",
]);
}
DB::commit();
return AbstractController::ResultSuccess("Update sprint success");
} catch (\Exception $e) {
DB::rollBack();
return AbstractController::ResultError("Update sprint failed: " . $e->getMessage());
}
}
}

View File

@ -8,7 +8,7 @@ use Illuminate\Database\Eloquent\Model;
class Sprint extends Model
{
protected $table = 'sprint';
protected $fillable = ['name', 'point', 'project_id', 'start_date', 'end_date', 'complete_date'];
protected $fillable = ['id', 'name', 'point', 'project_id', 'start_date', 'end_date', 'complete_date'];
public function criterias()
{
@ -45,7 +45,7 @@ class Sprint extends Model
'users_criterias.created_by as created_by',
'criterias.name as name',
'criterias.description as criteria_description',
'criterias.description as description',
);
}
@ -53,4 +53,13 @@ class Sprint extends Model
{
return $this->hasMany(UserCriteria::class);
}
public function sprintsCriterias()
{
return $this->hasMany(SprintCriteria::class, 'sprint_id');
}
public function usersCriterias()
{
return $this->hasMany(UserCriteria::class, 'sprint_id');
}
}

View File

@ -64,7 +64,7 @@ export const getAllSprintByIdBoard =
export const getAllIssuesByIdSprint =
API_URL + 'v1/admin/jira/get-all-issue-by-id-sprint'
export const updateSprintReview = API_URL + 'v1/admin/sprint-review/update'
export const updateSprintReview = API_URL + 'v1/admin/criterias/sprints'
//TestReport
export const getAllTestCase = API_URL + 'v1/admin/criterias/test-cases/getAll'

View File

@ -358,7 +358,7 @@ const Navbar = ({
style={{
display: 'flex',
flexFlow: 'column',
alignItems: 'center',
alignItems: 'flex-start',
}}
>
<Avatar

View File

@ -16,8 +16,10 @@ import {
Select,
Table,
Text,
Textarea,
TextInput,
Title,
Tooltip,
} from '@mantine/core'
import { notifications } from '@mantine/notifications'
import { useEffect, useState } from 'react'
@ -60,6 +62,8 @@ const SprintReview = () => {
// const [info, setInfo] = useState('')
const [activeBtn, setActiveBtn] = useState(false)
const [loading, setLoading] = useState(false)
const [loadingProject, setLoadingProject] = useState(true)
const [isDisabledSprint, setIsDisabledSprint] = useState(true)
const [criteriaSprint, setCriteriaSprint] = useState<DataCriteriaSprint[]>([])
const [criteriaUsers, setCriteriaUsers] = useState<DataCriteriaUsers[]>([])
const [infoBoard, setInfoBoard] = useState('')
@ -142,6 +146,7 @@ const SprintReview = () => {
const fetchData = async () => {
const result = await getListDataProject()
setDataProject(result ?? [])
setLoadingProject(false)
}
fetchData()
}, [])
@ -157,6 +162,9 @@ const SprintReview = () => {
}
}
fetchData()
} else {
setIsDisabledSprint(true)
setDataSprint([])
}
}, [filter.project])
@ -172,6 +180,7 @@ const SprintReview = () => {
item.completeDate !== '',
) ?? [],
)
setIsDisabledSprint(false)
}
}
fetchData()
@ -197,31 +206,35 @@ const SprintReview = () => {
setLoading(false)
}
}, [filter])
console.log(criteriaSprint, 'criteriaSprint')
// console.log(criteriaSprint, 'criteriaSprint')
// console.log(criteriaUsers, 'criteriaUsers')
// const handleCreate = async (values: TUser) => {
// try {
// const { id, ...data } = values
// const res = await post(createOrUpdateUser, data)
// if (res.status === true) {
// // setAction('review')
// form.reset()
// // getAll()
// // setInfo(JSON.stringify(res.data, null, 2))
// }
// } catch (error) {
// console.log(error)
// }
// }
const getAll = () => {
const fetchData = async () => {
const result = await getListCriteriasBySprint(filter.sprint)
if (result) {
setCriteriaSprint(result.criterias ?? [])
setCriteriaUsers(result.users ?? [])
setFinalPoint(result.point ?? '')
setLoading(false)
}
}
fetchData()
}
const handleUpdate = async () => {
const values = {}
const values = {
name: dataSprint.find((item: any) => item.id == filter.sprint)?.name,
project_id: filter.project,
criterias: criteriaSprint,
users: criteriaUsers,
finalPoint: finalPoint,
}
try {
const res = await update(
updateSprintReview,
`${updateSprintReview}/${filter.sprint}`,
values,
// , getAll
getAll,
)
if (res === true) {
setAction('')
@ -285,13 +298,28 @@ const SprintReview = () => {
Update
</Button>
</div>
<Box
style={{
marginTop: '20%',
textAlign: 'center',
display: loadingProject ? 'block' : 'none',
// display: 'none',
}}
>
<Loader size={'sm'} color="green" type="bars" m={'0 auto'} />
<Text fw={600} c={'gray'}>
Loading . . .
</Text>
</Box>
<Box display={'flex'} p={15}>
<Box
style={{
display: 'flex',
flexFlow: 'column',
}}
style={[
{
display: 'flex',
flexFlow: 'column',
},
loadingProject ? { display: 'none' } : {},
]}
w={'50%'}
>
<Box w="100%" display={'flex'}>
@ -304,6 +332,7 @@ const SprintReview = () => {
Project:
</Text>
<Select
searchable
clearable
w="60%"
value={filter.project}
@ -326,6 +355,8 @@ const SprintReview = () => {
Sprint:
</Text>
<Select
disabled={isDisabledSprint}
searchable
clearable
w="40%"
value={filter.sprint}
@ -419,12 +450,15 @@ const SprintReview = () => {
<Table.Tr key={index} style={rowStyle}>
{/* TextInput cho trường name */}
<Table.Td>
<TextInput
{/* <TextInput
value={item.name}
onChange={(e) =>
handleInputChange(index, 'name', e.target.value)
}
/>
/> */}
<Tooltip multiline label={item.description}>
<span>{item.name}</span>
</Tooltip>
</Table.Td>
{/* TextInput cho trường expect */}
<Table.Td>
@ -446,11 +480,15 @@ const SprintReview = () => {
</Table.Td>
{/* TextInput cho trường note */}
<Table.Td>
<TextInput
<Textarea
autosize
placeholder="Good performance"
value={item.note}
onChange={(e) =>
handleInputChange(index, 'note', e.target.value)
}
minRows={2}
maxRows={4}
/>
</Table.Td>
<Table.Td>
@ -562,20 +600,21 @@ const SprintReview = () => {
)}
<Table.Td>
<TextInput
value={item.name}
onChange={(e) =>
handleInputChangeUsers(index, 'name', e.target.value)
}
/>
<Tooltip multiline label={item.description}>
<span>{item.name}</span>
</Tooltip>
</Table.Td>
<Table.Td>
<TextInput
<Textarea
autosize
placeholder="Good performance"
value={item.note}
onChange={(e) =>
handleInputChangeUsers(index, 'note', e.target.value)
}
minRows={2}
maxRows={4}
/>
</Table.Td>
@ -620,7 +659,7 @@ const SprintReview = () => {
position={{ top: 30, right: 10 }}
>
<Text className={classes.dialogText} size="sm" mb="xs" fw={500}>
Do you want to delete this record?
Do you want to update?
<Group justify="center" m={10}>
<Button
disabled={activeBtn}
@ -628,6 +667,7 @@ const SprintReview = () => {
size="xs"
variant="light"
onClick={async () => {
setLoading(true)
setActiveBtn(true)
await handleUpdate()
setActiveBtn(false)

View File

@ -67,7 +67,8 @@ const TestReport = () => {
})
const [disableBtn, setDisableBtn] = useState(false)
const [loading, setLoading] = useState(false)
const [loadingProject, setLoadingProject] = useState(true)
const [isDisabledSprint, setIsDisabledSprint] = useState(true)
const [infoBoard, setInfoBoard] = useState('')
const [filter, setFilter] = useState({
project: '',
@ -150,6 +151,7 @@ const TestReport = () => {
const fetchData = async () => {
const result = await getListDataProject()
setDataProject(result ?? [])
setLoadingProject(false)
}
fetchData()
}, [])
@ -165,6 +167,10 @@ const TestReport = () => {
}
}
fetchData()
} else {
setIsDisabledSprint(true)
setDataSprint([])
setInfoBoard('')
}
}, [filter.project])
@ -180,6 +186,7 @@ const TestReport = () => {
item.completeDate !== '',
) ?? [],
)
setIsDisabledSprint(false)
}
}
fetchData()
@ -279,13 +286,28 @@ const TestReport = () => {
+ Add
</Button>
</div>
<Box
style={{
marginTop: '20%',
textAlign: 'center',
display: loadingProject ? 'block' : 'none',
// display: 'none',
}}
>
<Loader size={'sm'} color="green" type="bars" m={'0 auto'} />
<Text fw={600} c={'gray'}>
Loading . . .
</Text>
</Box>
<Box display={'flex'} p={15}>
<Box
style={{
display: 'flex',
flexFlow: 'column',
}}
style={[
{
display: 'flex',
flexFlow: 'column',
},
loadingProject ? { display: 'none' } : {},
]}
w={'50%'}
>
<Box w="100%" display={'flex'}>
@ -298,6 +320,7 @@ const TestReport = () => {
Project:
</Text>
<Select
searchable
clearable
w="60%"
value={filter.project}
@ -320,6 +343,8 @@ const TestReport = () => {
Sprint:
</Text>
<Select
disabled={isDisabledSprint}
searchable
clearable
w="40%"
value={filter.sprint}
@ -369,7 +394,7 @@ const TestReport = () => {
Loading . . .
</Text>
</Box>
<Box style={loading ? { display: 'none' } : {}}>
<Box style={loading || !filter.sprint ? { display: 'none' } : {}}>
{/* Tiêu đề Criteria for Sprint */}
<Title order={5} ml="xs">
List bug:
@ -484,12 +509,20 @@ const TestReport = () => {
/>
<Textarea
style={{ marginTop: '10px' }}
autosize
minRows={2}
maxRows={4}
label="Input"
required
value={form.values.input}
onChange={(e) => form.setFieldValue('input', e.target.value)}
/>
<Textarea
style={{ marginTop: '10px' }}
autosize
minRows={2}
maxRows={4}
label="Expect Output"
required
value={form.values.expect_output}
@ -498,6 +531,10 @@ const TestReport = () => {
}
/>
<Textarea
style={{ marginTop: '10px' }}
autosize
minRows={2}
maxRows={4}
label="Actual Output"
required
value={form.values.actual_output}
@ -506,7 +543,7 @@ const TestReport = () => {
}
/>
<Box display={'flex'}>
<Box display={'flex'} style={{ marginTop: '10px' }}>
<Box style={{ display: 'flex', flexFlow: 'column' }} w={'45%'}>
<TextInput
label="Issue ID On Jira"
@ -533,6 +570,10 @@ const TestReport = () => {
</Box>
</Box>
<Textarea
style={{ marginTop: '10px' }}
autosize
minRows={2}
maxRows={4}
label="Note"
required
value={form.values.note}
@ -583,25 +624,34 @@ const TestReport = () => {
<Textarea
readOnly
style={{ pointerEvents: 'none' }}
style={{ marginTop: '10px' }}
autosize
minRows={2}
maxRows={4}
label="Input"
value={item.input}
/>
<Textarea
readOnly
style={{ pointerEvents: 'none' }}
style={{ marginTop: '10px' }}
label="Expect Output"
value={item.expect_output}
autosize
minRows={2}
maxRows={4}
/>
<Textarea
readOnly
style={{ pointerEvents: 'none' }}
style={{ marginTop: '10px' }}
autosize
minRows={2}
maxRows={4}
label="Actual Output"
value={item.actual_output}
/>
<Box display={'flex'}>
<Box display={'flex'} style={{ marginTop: '10px' }}>
<Box style={{ display: 'flex', flexFlow: 'column' }} w={'45%'}>
<TextInput
readOnly
@ -627,7 +677,10 @@ const TestReport = () => {
</Box>
<Textarea
readOnly
style={{ pointerEvents: 'none' }}
style={{ marginTop: '10px' }}
autosize
minRows={2}
maxRows={4}
label="Note"
value={item.note}
/>