Merge branch 'JIWOO'
This commit is contained in:
commit
9e8fa154fa
10
src/main/java/seed/com/gtm/dao/ProgressclipDao.java
Normal file
10
src/main/java/seed/com/gtm/dao/ProgressclipDao.java
Normal file
@ -0,0 +1,10 @@
|
||||
package seed.com.gtm.dao;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface ProgressclipDao {
|
||||
|
||||
public List<Map<String, Object>> memList(Map<String, Object> paramMap);
|
||||
|
||||
}
|
||||
21
src/main/java/seed/com/gtm/dao/ProgressclipDaoImpl.java
Normal file
21
src/main/java/seed/com/gtm/dao/ProgressclipDaoImpl.java
Normal file
@ -0,0 +1,21 @@
|
||||
package seed.com.gtm.dao;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.ibatis.session.SqlSession;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public class ProgressclipDaoImpl implements ProgressclipDao {
|
||||
|
||||
@Autowired
|
||||
private SqlSession sqlSession;
|
||||
|
||||
public List<Map<String, Object>> memList(Map<String, Object> paramMap){
|
||||
|
||||
return sqlSession.selectList("progress.select", paramMap);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
package seed.com.gtm.progressclip;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/gtm/case")
|
||||
public class progressclipController {
|
||||
|
||||
@Autowired
|
||||
progressclipService progressclipService;
|
||||
|
||||
@RequestMapping(value="/progressclip/clip.do")
|
||||
public String memList(ModelMap model, HttpSession session, @RequestParam Map<String,Object> paramMap){
|
||||
|
||||
session.setAttribute("siteIdx", "case");
|
||||
|
||||
paramMap.put("memberId", session.getAttribute("memberId"));
|
||||
paramMap.put("memberDept", session.getAttribute("memberDept"));
|
||||
|
||||
List<Map<String, Object>> memList = progressclipService.memList(paramMap);
|
||||
model.addAttribute("memList", memList);
|
||||
|
||||
return "/seed/_extra/gtm/progressclip/clip";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package seed.com.gtm.progressclip;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import seed.com.gtm.dao.ProgressclipDao;
|
||||
|
||||
@Service
|
||||
public class progressclipService {
|
||||
@Autowired
|
||||
private ProgressclipDao dao;
|
||||
|
||||
public List<Map<String, Object>> memList(Map<String, Object> paramMap) {
|
||||
return dao.memList(paramMap);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<mapper namespace="progress">
|
||||
|
||||
<select id="select" parameterType="java.util.HashMap" resultType="java.util.HashMap">
|
||||
|
||||
SELECT MEMBER_ID, MEMBER_NAME FROM T_MEMBER WHERE MEMBER_DEPT = #{memberDept}
|
||||
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@ -38,17 +38,17 @@
|
||||
AND (DEL_GUBUN = 'N' OR DEL_GUBUN = 'R')
|
||||
AND RCEPT_NO IS NOT NULL
|
||||
<choose>
|
||||
<when test='memberDept eq "1030000000" and memberPosition neq "0911000000"'>
|
||||
<when test='memberDept eq "1030000000" and memberPosition neq "0911000000"'> <!-- 유통팀(?)이면서 팀장이 아닌 경우 -->
|
||||
<choose>
|
||||
<when test='searchMa == "Y"'>
|
||||
<when test='searchMa == "Y"'> <!-- 배정 완료 -->
|
||||
AND STATE = '0302000000'
|
||||
AND EXAMINER_ASSIGN IS NOT NULL
|
||||
</when>
|
||||
<when test='searchMa == "P"'>
|
||||
<when test='searchMa == "P"'> <!-- 배정 중 -->
|
||||
AND STATE = '0302000000'
|
||||
AND EXAMINER_ASSIGN IS NULL
|
||||
</when>
|
||||
<otherwise>
|
||||
<otherwise> <!-- 미배정 -->
|
||||
AND STATE = '0301000000'
|
||||
AND PRESIDENT_ASSIGN IS NULL
|
||||
AND PRESIDENT_ASSIGN_DT IS NULL
|
||||
@ -60,7 +60,7 @@
|
||||
</otherwise>
|
||||
</choose>
|
||||
</when>
|
||||
<when test='memberDept eq "1010000000"'>
|
||||
<when test='memberDept eq "1010000000"'> <!-- 민원상담센터 -->
|
||||
<choose>
|
||||
<when test='searchMa == "Y"'>
|
||||
AND STATE = '0302000000'
|
||||
@ -92,7 +92,7 @@
|
||||
AND TIMHDER_ASSIGN_DT IS NULL
|
||||
AND EXAMINER_ASSIGN IS NULL
|
||||
</when> -->
|
||||
<when test='memberDept eq "1001000000" and memberPosition eq "0903000000"'>
|
||||
<when test='memberDept eq "1001000000" and memberPosition eq "0903000000"'> <!-- 한국공정거래조정원 소속 부원장인 경우 -->
|
||||
<choose>
|
||||
<when test='searchMa == "Y"'>
|
||||
AND STATE = '0302000000'
|
||||
@ -114,7 +114,7 @@
|
||||
</otherwise>
|
||||
</choose>
|
||||
</when>
|
||||
<when test='memberDept eq "1003000000" and memberPosition eq "0910000000"'><!-- 실장 -->
|
||||
<when test='memberDept eq "1003000000" and memberPosition eq "0910000000"'><!-- 분쟁조정실 소속 실장 -->
|
||||
AND STATE = '0302000000'
|
||||
AND PRESIDENT_ASSIGN IS NOT NULL
|
||||
AND PRESIDENT_ASSIGN_DT IS NOT NULL
|
||||
|
||||
@ -0,0 +1,94 @@
|
||||
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
|
||||
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
|
||||
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
|
||||
<form:form name="searchFrm" id="searchFrm" action="" method="post">
|
||||
<input type="hidden" id="CLIPDATA_MEMBER_ID" name="CLIPDATA_MEMBER_ID" value="">
|
||||
<input type="hidden" id="CLIPDATA_MEMBER_NAME" name="CLIPDATA_MEMBER_NAME" value="">
|
||||
<input type="hidden" id="CLIPDATA_MEMBER_DEPT" name="CLIPDATA_MEMBER_DEPT" value="">
|
||||
<input type="hidden" id="CLIPDATA_MEMBER_DATEST2" name="CLIPDATA_MEMBER_DATEST2" value="">
|
||||
<input type="hidden" id="crfNm1" name="crfNm" value="">
|
||||
</form:form>
|
||||
<input type="hidden" name="memberDept" id="memberDept" value="${memberDept}">
|
||||
<input type="hidden" name="examiner" id="examiner" value="">
|
||||
<input type="hidden" name="memberName" id="memberName" value="">
|
||||
<p id="showName">
|
||||
|
||||
</p>
|
||||
<div class="sch-content-wrap clear">
|
||||
<fieldset>
|
||||
<select name="examiner" id="selExamier" class="sch-select" title="조사관선택" onchange="insertExaminer();">
|
||||
<option value="rem">선택</option>
|
||||
<c:if test="${memList != '' }">
|
||||
<c:forEach items="${memList}" var="list" varStatus="status">
|
||||
<option value="${list.MEMBER_ID}">${list.MEMBER_NAME}</option>
|
||||
</c:forEach>
|
||||
</c:if>
|
||||
</select>
|
||||
<input type="text" id="dateSt2" name="dateSt2" value="" class="sch-text" style="width:10px" title="검색">
|
||||
<button type="button" class="btn-page-sch" title="엑셀다운로드" onclick="clipCall2('progress')">엑셀다운로드</button>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
//클립리포트 호출
|
||||
|
||||
function insertExaminer(){
|
||||
var type = $("#selExamier").val();
|
||||
var name = $("#selExamier > option:selected").text();
|
||||
|
||||
if(type == 'rem'){
|
||||
$("#examiner").val("");
|
||||
$("#memberName").val("");
|
||||
$("#showName").text("");
|
||||
}else{
|
||||
var examinerArr = $("#examiner").val();
|
||||
var nameArr = $("#memberName").val();
|
||||
|
||||
if(examinerArr == ""){
|
||||
examinerArr += type;
|
||||
nameArr += name;
|
||||
}else{
|
||||
examinerArr += ","+type;
|
||||
nameArr += ", "+name;
|
||||
}
|
||||
$("#memberName").val(nameArr);
|
||||
$("#examiner").val(examinerArr);
|
||||
$("#showName").text(nameArr);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
function clipCall2(crfNm){
|
||||
|
||||
if($("#examiner").val() == ""){
|
||||
alert("조사관을 선택해주세요");
|
||||
$("#examiner").focus();
|
||||
return false;
|
||||
}else if($("#dateSt2").val() == ""){
|
||||
alert("날짜를 입력해주세요");
|
||||
$("#dateSt2").focus();
|
||||
return false;
|
||||
}
|
||||
|
||||
$("#crfNm1").val(crfNm);
|
||||
$("#CLIPDATA_MEMBER_ID").val($("#examiner").val());
|
||||
//$("#CLIPDATA_MEMBER_NAME").val($("#examiner > option:selected").text());
|
||||
$("#CLIPDATA_MEMBER_DEPT").val($("#memberDept").val());
|
||||
$("#CLIPDATA_MEMBER_DATEST2").val($("#dateSt2").val());
|
||||
|
||||
var f = document.searchFrm;
|
||||
f.target = "blank";
|
||||
f.action = "/clip/ClipReport4/ClipReportViewer/ClipReport4.do";
|
||||
f.submit();
|
||||
f.target = "_self";
|
||||
}
|
||||
|
||||
$(function(){
|
||||
$("#dateSt2").datepicker({
|
||||
dateFormat:'yy-mm-dd',
|
||||
maxDate:0
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
@ -0,0 +1,212 @@
|
||||
<%@ page contentType="text/html;charset=utf-8" %>
|
||||
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
|
||||
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
|
||||
|
||||
<!doctype html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="robots" content="noindex">
|
||||
<meta id="viewport" name="viewport" content="initial-scale=1.0, width=device-width, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
|
||||
<!--[if IE]>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<![endif]-->
|
||||
<title>자료요구현황</title>
|
||||
|
||||
<link rel="shortcut icon" href="/img/favicon.ico" type="image/x-icon">
|
||||
<link rel="icon" href="/img/favicon.ico" type="image/x-icon">
|
||||
|
||||
<!-- css -->
|
||||
<link rel="stylesheet" href="/css/seed.reset.css">
|
||||
<link rel="stylesheet" href="/css/seed.layout.css">
|
||||
<link rel="stylesheet" href="/css/layout.css">
|
||||
<link rel="stylesheet" href="/css/button.css">
|
||||
<link rel="stylesheet" href="/css/seed.contents.css">
|
||||
<link rel="stylesheet" href="/css/seed.mediaquery.css">
|
||||
<link rel="stylesheet" href="/css/lib/jquery.mCustomScrollbar.min.css">
|
||||
<link rel="stylesheet" href="/css/jquery-ui.css"/>
|
||||
<link rel="stylesheet" href="/css/smartPop.css"/>
|
||||
<link rel="stylesheet" href="/css/space.css"/>
|
||||
<link rel="stylesheet" href="/css/picker.default.css">
|
||||
<link rel="stylesheet" href="/css/picker.default.date.css">
|
||||
<link rel="stylesheet" href="/css/case/common.css">
|
||||
<!-- css -->
|
||||
|
||||
<!--[if lt IE 9]>
|
||||
<script src="/js/lib/polyfill/IE9.js"></script>
|
||||
<script src="/js/lib/polyfill/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
<!-- js -->
|
||||
<!-- 라이브러리, 플러그인 -->
|
||||
<script src="/js/lib/jquery-1.9.1.min.js"></script>
|
||||
<script src="/js/lib/jquery-ui.min.js"></script>
|
||||
<script src="/js/lib/jquery.blockUI.js"></script>
|
||||
|
||||
<script src="/js/jquery.form.js"></script>
|
||||
<script src="/js/commonFileUtil.js"></script>
|
||||
<script src="/js/common_XHR.js"></script>
|
||||
<script src="/js/common.js"></script>
|
||||
<!-- js -->
|
||||
</head>
|
||||
<body>
|
||||
<h2 class="mb10 mt30">■ 자료요구현황</h2>
|
||||
<form name="frm" id="frm" action="" method="post">
|
||||
<input type="hidden" name="caseNo" value="${param.caseNo }">
|
||||
<div class="form-wrap">
|
||||
<div class="table-layout br-none">
|
||||
<table>
|
||||
<caption>자료 요구현황 테이블입니다.</caption>
|
||||
<colgroup>
|
||||
<col style="width:10%">
|
||||
<col style="width:10%">
|
||||
<col style="width:10%">
|
||||
<col style="width:10%">
|
||||
<col style="width:10%">
|
||||
<col style="width:10%">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">자료요구일자</th>
|
||||
<th scope="col">자료마감일자</th>
|
||||
<th scope="col">자료이행일자</th>
|
||||
<th scope="col">대상</th>
|
||||
<th scope="col">제출여부</th>
|
||||
<th scope="col">삭제</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="innertBox">
|
||||
<tr>
|
||||
<td colspan="6">등록된 데이터가 없습니다.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ac mt20 mb60">
|
||||
<button type="button" id="fileReqIn" class="btn-default violet responsive">저장</button>
|
||||
<button type="button" class="btn-default responsive returnList" onclick="window.close()">닫기</button>
|
||||
</div>
|
||||
</form>
|
||||
<form name="frmTemp" id="frmTemp" action="" method="post">
|
||||
<input type="hidden" name="seqNo" id="seqNo" value="">
|
||||
<input type="hidden" name="dataReqdt" id="dataReqdt" value="">
|
||||
<input type="hidden" name="dataEnddt" id="dataEnddt" value="">
|
||||
<input type="hidden" name="dataDt" id="dataDt" value="">
|
||||
<input type="hidden" name="dataCheck" id="dataCheck" value="">
|
||||
</form>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function(){
|
||||
|
||||
var dataLen = "";
|
||||
|
||||
$("#typeTemp").val("L");
|
||||
var param = jQuery('#frm').serialize();
|
||||
url = "/gtm/case/trublprocessmng/ajax/DataRequestList.do";
|
||||
XHR2(url, param, function(r){
|
||||
if(r.data.length > 0){
|
||||
dataLen = r.data.length;
|
||||
var htmlVal = "";
|
||||
for(var i = 0; i < r.data.length; i++){
|
||||
|
||||
var dataEnddt = "";
|
||||
if(r.data[i].dataEnddt != undefined){
|
||||
dataEnddt = r.data[i].dataEnddt;
|
||||
}
|
||||
|
||||
var dataDt = "";
|
||||
if(r.data[i].dataDt != undefined){
|
||||
dataDt = r.data[i].dataDt;
|
||||
}
|
||||
|
||||
htmlVal +='<tr>';
|
||||
htmlVal +='<td><input type="text" class="datepicker" name="dataReqdt_'+i+'" id="dataReqdt_'+i+'" value="'+r.data[i].dataReqdt+'" maxlength="10"></td>';
|
||||
htmlVal +='<td><input type="text" class="datepicker" name="dataEnddt_'+i+'" id="dataEnddt_'+i+'" value="'+dataEnddt+'" maxlength="10"></td>';
|
||||
htmlVal +='<td><input type="text" class="datepicker" name="dataDt_'+i+'" id="dataDt_'+i+'" value="'+dataDt+'" maxlength="10"></td>';
|
||||
htmlVal +='<td>'+r.data[i].targetName+'</td>';
|
||||
htmlVal +='<td>';
|
||||
htmlVal +='<select name="dataCheck_'+i+'" id="dataCheck_'+i+'">';
|
||||
if(r.data[i].dataCheck == "Y"){
|
||||
htmlVal +='<option value="N">미제출</option>';
|
||||
htmlVal +='<option value="Y" selected="selected">제출</option>';
|
||||
}else{
|
||||
htmlVal +='<option value="N" selected="selected">미제출</option>';
|
||||
htmlVal +='<option value="Y">제출</option>';
|
||||
}
|
||||
htmlVal +='</select>';
|
||||
htmlVal +='</td>';
|
||||
htmlVal +='<td>';
|
||||
htmlVal +='<button type="button" class="btn-default" onclick="dataDel('+r.data[i].seqNo+');">삭제</button>';
|
||||
htmlVal +='<input type="hidden" name="seqNo_'+i+'" id="seqNo_'+i+'" value="'+r.data[i].seqNo+'">';
|
||||
htmlVal +='</td>';
|
||||
htmlVal +='</tr>';
|
||||
}
|
||||
|
||||
$("#innertBox").html(htmlVal);
|
||||
}else{
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
$("#fileReqIn").click(function(){
|
||||
$("#typeTemp").val("U");
|
||||
|
||||
for(var i = 0; i < dataLen; i++){
|
||||
|
||||
$("#seqNo").val($("#seqNo_"+i).val());
|
||||
$("#dataReqdt").val($("#dataReqdt_"+i).val());
|
||||
$("#dataEnddt").val($("#dataEnddt_"+i).val());
|
||||
$("#dataDt").val($("#dataDt_"+i).val());
|
||||
$("#dataCheck").val($("#dataCheck_"+i).val());
|
||||
var param = jQuery('#frmTemp').serialize();
|
||||
url = "/gtm/case/trublprocessmng/ajax/DataRequestUp.do";
|
||||
XHR2(url, param, function(r){
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
alert("저장 되었습니다.");
|
||||
window.close();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function dataDel(delId){
|
||||
if(confirm("정말 삭제 하시겠습니까?")){
|
||||
$("#seqNo").val(delId);
|
||||
var param = jQuery('#frmTemp').serialize();
|
||||
url = "/gtm/case/trublprocessmng/ajax/DataRequestDel.do";
|
||||
XHR2(url, param, function(r){
|
||||
|
||||
});
|
||||
|
||||
alert("삭제가 완료 되었습니다");
|
||||
location.reload(true);
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<!-- js -->
|
||||
<!-- 라이브러리, 플러그인 -->
|
||||
<script src="/js/lib/jquery.pjax.js"></script>
|
||||
<script src="/js/lib/modernizr-custom.js"></script>
|
||||
<script src="/js/lib/jquery.mCustomScrollbar.concat.min.js"></script>
|
||||
<script src="/js/lib/jquery.bxslider.min.js"></script>
|
||||
|
||||
<script src="/js/lib/picker.js"></script>
|
||||
<script src="/js/lib/picker.date.js"></script>
|
||||
<script src="/js/lib/legacy.js"></script>
|
||||
<script src="/js/lib/base64.js"></script>
|
||||
<!-- 라이브러리, 플러그인 끝 -->
|
||||
<script src="/js/jquery.seed.js"></script>
|
||||
<script src="/js/seed.common.js"></script>
|
||||
<script src="/js/seed.app.js"></script>
|
||||
|
||||
<script src="/js/DateTimePicker.js"></script>
|
||||
<script src="/js/jquery.selectboxes.js"></script>
|
||||
<!-- js -->
|
||||
</body>
|
||||
</html>
|
||||
@ -3410,13 +3410,16 @@ function fn_caseComment_del(type, gubun, seq, e) {
|
||||
<button type="button" class="btn-default fl" onclick="clipCall('reqPrint', '', '');" style="margin-left: 30px">신청서출력</button>
|
||||
</div>
|
||||
<h2 class="mt20">분쟁사건 처리관리</h2>
|
||||
<!-- 조사관, 팀장, 관리자 -->
|
||||
<c:if test="${memberPosition == '0912000000' || memberPosition == '0911000000' || memberPosition == '0915000000'}">
|
||||
<div class="mt20 mb7 clear">
|
||||
|
||||
<!-- 접수상태이면서 조사관 or 팀장-->
|
||||
<c:if test="${masterData.statePro == '0401000000' && (memberPosition == '0912000000' || memberPosition == '0911000000' || memberId =='seed')}">
|
||||
<!-- 접수 -->
|
||||
<button type="button" class="btn-default fl" onclick="nanumCall('1', '<c:out value="${masterData.caseNo }"/> 사건착수보고');" style="background-color: #008299">착수보고</button>
|
||||
<!-- 공정거래, 하도급거래, 대리점거래 -->
|
||||
<c:if test="${masterData.caseGubun == '0201000000' || masterData.caseGubun == '0203000000' || masterData.caseGubun == '0206000000'}">
|
||||
<!-- 공정거래팀인 경우 통지, 다른팀은 보고-->
|
||||
<c:choose>
|
||||
<c:when test="${memberDept == '1004000000'}">
|
||||
<button type="button" class="btn-default fl" onclick="nanumCall('6', '분쟁조정 접수사실 통지');" style="background-color: #008299;margin-left: 10px">공정위 접수통지</button>
|
||||
|
||||
@ -435,7 +435,7 @@
|
||||
<input type="checkbox" id="chk" value="${list.rceptNo }" />
|
||||
</td>
|
||||
<td><a
|
||||
href="javascript:goPage('edit','${list.rceptNo}', '${list.delGubun}');"
|
||||
href="javascript:goPage('view','${list.rceptNo}', '${list.delGubun}');"
|
||||
<c:if test="${list.delGubun == 'R'}">style='color: red'</c:if>>${list.rceptNo}</a>
|
||||
</td>
|
||||
<!-- tempSrc(조건없이 임시 사용) -->
|
||||
|
||||
Loading…
Reference in New Issue
Block a user